Skip to content

React Tutorial Forms and Validation

Accessible Forms in React: Labels, Fieldsets and Autocomplete

Lesson 80 of 156 3 min read By Deepak Paswan Updated Oct 10, 2026

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:

App.jsx
JSX
function App() {
  return (
    <div>
      {/* 1. Wrapping the input */}
      <label>
        Email address
        <input type="email" name="email" autoComplete="email" aria-label="Email address" />
      </label>
    </div>
  );
}
App.jsx
JSX
// 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.)

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”).

App.jsx
JSX
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.

FieldMarkup
Emailtype="email" autoComplete="email"
Phonetype="tel" autoComplete="tel"
NameautoComplete="name" (or given-name, family-name)
PostcodeautoComplete="postal-code"
New passwordtype="password" autoComplete="new-password"
Current passwordtype="password" autoComplete="current-password"
One-time codeinputMode="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.

App.jsx
JSX
<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 outline without 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, autoComplete and inputMode
  • [ ] Errors are linked with aria-describedby and 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, fieldset and legend, correct input types and autoComplete.
  • Connect hints and errors with ARIA and announce results.
  • Test with the keyboard; keep focus visible.