Chapter 6 · Practice 2 of 4
Give every form control a visible label
From Accessibility
Why it matters
Every input needs a label that is connected to it, either by wrapping the input in <label> or by matching htmlFor to the input's id.
- Placeholders are not labels: they disappear when the user types and often have low contrast.
- Generate ids with
useIdso they stay unique when the component renders several times and match between server and client. - Connect error messages with
aria-describedbyso screen readers read them with the field.
Example
function EmailField({ error }: { error?: string }) {
const id = useId();
return (
<>
<label htmlFor={id}>Email</label>
<input id={id} name="email" aria-describedby={error ? id + '-err' : undefined} />
{error && <p id={id + '-err'}>{error}</p>}
</>
);
}