useId links label, hint and error in every instance
Two Fields, two different ids, no collisions. The last line is what a screen reader reads after the field's name: the hint, then the error. Styling input[aria-invalid="true"] keeps the visual state and the announced state in one place.
ARIA attributes add roles, names, relationships and states that HTML can't express; in React you write them as hyphenated aria-* props, connect elements with ids from useId, and announce async changes with live regions.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a Toggle button component for "Mute" that exposes its on/off state to screen readers.
Write a SaveStatus component that announces Saving…, then Saved after a fake 500 ms save.
Explain it without notes
How do you write ARIA attributes in JSX, and how does React render boolean values for them?
Why does a reusable component need useId for accessibility, and what properties does useId guarantee?
What is a live region, and what mistake makes React live regions silent?
What is the first rule of ARIA? Give two cases where ARIA is genuinely needed.
Preview
Console
Nothing logged yet.Expected console output
Name → id _r_0_ | describedby: _r_0_-hint
Email → id _r_1_ | describedby: _r_1_-hint
after blur: aria-invalid = true
description read out: As on your ID card. Name is required.