How to build an accessible Field in React
Generates stable ids with useId and links them through htmlFor and aria-describedby; the error carries role="alert" so it is announced on appearance, and aria-invalid marks the control invalid for assistive tech
When to use a Field
- Pairing a single control with its label, helper text, and validation message
- Wiring aria-describedby / aria-invalid automatically instead of by hand
When not to use it
- A standalone control that needs no label, description, or error
- Grouping multiple related controls — use a <fieldset> with a <legend>
Accessibility
Role group, verified at WCAG 2.2-AA.
Common mistakes
Avoid: <Field label="Email"><Input label="Email" /></Field>
Prefer: <Field label="Email"><Input /></Field>
Field owns the label; a labelled child (Input, Textarea, NativeSelect, …) renders a SECOND <label> for the same control. Omit the child's label inside a Field (dev builds warn).
Avoid: <Field label="Email"><Input /><Input /></Field>
Prefer: <Field label="Email"><Input /></Field>
Field clones exactly one child control to inject its id and aria wiring; multiple children break the association
Avoid: <div><label>Email</label><input aria-invalid /><span>Error</span></div>
Prefer: <Field label="Email" error="Error"><Input /></Field>
Hand-wired markup easily drifts: missing ids, no role="alert", mismatched aria-describedby — Field keeps them in sync
Example
<Field label="Email"><Input type="email" /></Field>