Runnable lesson · press Try it on the examples to edit and run them here.
Forms are where accessibility problems hurt most, because a form you cannot use is a form you cannot finish. React gives you full control over the markup, so the work is to use the right HTML.
1. Every input needs a label
A placeholder is not a label: it disappears as you type and is often low contrast. Use a real <label>, connected in one of two ways:
function App() {
return (
<div>
{/* 1. Wrapping the input */}
<label>
Email address
<input type="email" name="email" autoComplete="email" aria-label="Email address" />
</label>
</div>
);
}
// 2. Using htmlFor and id (use useId so the id is unique)
const id = useId();
<label htmlFor={id}>Email address</label>
<input id={id} type="email" />
In JSX the attribute is htmlFor, not for. An icon-only field needs an aria-label. (The playground examples also add aria-label so that the automatic tests can find them; with a visible label you usually do not need both.)
2. Group related inputs
Radio buttons and sets of checkboxes belong in a fieldset with a legend. Screen readers announce the legend before each option (“Shipping speed, Standard, radio button, 1 of 2”).
function App() {
const [speed, setSpeed] = React.useState("standard");
return (
<fieldset>
<legend>Shipping speed</legend>
<label><input type="radio" name="speed" value="standard" checked={speed === "standard"} onChange={(e) => setSpeed(e.target.value)} /> Standard</label>
<label><input type="radio" name="speed" value="express" checked={speed === "express"} onChange={(e) => setSpeed(e.target.value)} /> Express</label>
<p>Chosen: {speed}</p>
</fieldset>
);
}
3. Use the right input type and autocomplete
The type brings the right keyboard on phones and built-in checks; autoComplete lets browsers and password managers fill the field, which is a requirement for accessible authentication.
| Field | Markup |
|---|---|
type="email" autoComplete="email" | |
| Phone | type="tel" autoComplete="tel" |
| Name | autoComplete="name" (or given-name, family-name) |
| Postcode | autoComplete="postal-code" |
| New password | type="password" autoComplete="new-password" |
| Current password | type="password" autoComplete="current-password" |
| One-time code | inputMode="numeric" autoComplete="one-time-code" |
In JSX these are camelCase: autoComplete, inputMode, maxLength, readOnly.
4. Mark required fields, in the markup and in words
Use the required attribute so assistive technology announces it, and also say it visibly. If most fields are required, say that once at the top, and mark the optional ones instead.
<label>
Name <span aria-hidden="true">*</span>
<input required />
</label>
<p><span aria-hidden="true">*</span> Required field</p>
5. Hints and errors
Link help text and errors with aria-describedby, and do not rely on colour. See Validation Messages.
6. Keyboard and focus
- Use real
<button>elements. They work with Enter and Space and can be focused. - Keep a visible focus style. Never remove
outlinewithout replacing it; see Accessible Focus States. - The order of fields in the code should match the visual order, so Tab moves logically.
- Do not trap focus, and do not make the user fight custom dropdowns. A native
<select>is almost always the most accessible choice.
7. Enough size and contrast
Make click targets at least about 24 by 24 CSS pixels (larger is better on touch). Text and borders need enough contrast with the background.
8. Announce results
After submitting, tell users what happened with role="status" for success and role="alert" for failure, and move focus to the first error. See Form Submission.
A checklist
- [ ] Every control has a visible, connected label
- [ ] Related controls are grouped with
fieldset/legend - [ ] Correct
type,autoCompleteandinputMode - [ ] Errors are linked with
aria-describedbyand not colour-only - [ ] Focus is visible and the order is logical
- [ ] The form works with the keyboard alone
You can check a page quickly by pressing Tab through the form, and with a free tool such as the browser’s accessibility panel or an automated checker. Automated tools find only part of the problems, so also test by hand. More in Accessibility in React.
Key points
- Use real labels,
fieldsetandlegend, correct input types andautoComplete. - Connect hints and errors with ARIA and announce results.
- Test with the keyboard; keep focus visible.