Accessible React Interfaces
Accessible React Interfaces
A student uses a keyboard to complete a form, while another student uses a screen reader. A visually attractive field with only placeholder text may leave both unsure what information to enter or whether submission succeeded.
An accessible interface lets people perceive, understand and operate it using different abilities and tools. React renders HTML, so ordinary semantic elements, labels and keyboard behavior remain the foundation.
Name controls
Associate a persistent label with each field.
Support operation
Use native buttons and predictable keyboard behavior.
Explain results
Provide feedback that can be understood without color alone.
Follow the user’s interaction
Control
A label identifies the student-name field.
Action
A native submit button starts the greeting.
Feedback
A status message describes the result.
Start with what the control means
A label names a field. In JSX, htmlFor points to the input’s id, allowing the browser to connect them. A visible label remains after the user types; a placeholder may disappear. A real button supports keyboard activation without inventing click handlers on a generic div.
The example uses useId to generate linked IDs safely when more than one instance of a component appears. aria-describedby points to supplementary instructions, while the label remains the control’s name. React’s input documentation explains label associations, and the useId reference describes generated accessibility IDs.
A labeled form with visible feedback
The field starts with Maya. Create greeting submits the form, prevents this local demo from navigating away and sets a result message. The status region is present from the initial render so later changes have a stable place to appear. It is not a replacement for good wording or manual accessibility checks.
Try entering a different name and submitting by keyboard as well as by mouse. The instructions are linked to the input, and the outcome does not depend on the student recognizing a particular color.
React · JavaScript with JSX
import React, { useId, useState } from 'react';
export default function App() {
const nameId = useId();
const helpId = useId();
const [name, setName] = useState('Maya');
const [message, setMessage] = useState('No greeting yet');
function submit(event) {
event.preventDefault();
setMessage(name.trim() ? 'Hello, ' + name.trim() : 'Enter a student name');
}
return <main><h1>Student greeting</h1><form onSubmit={submit}>
<label htmlFor={nameId}>Student name</label>
<input id={nameId} aria-describedby={helpId} value={name}
onChange={event => setName(event.target.value)} />
<p id={helpId}>Use the name shown on the class register.</p>
<button type="submit">Create greeting</button>
</form><p role="status">{message}</p></main>;
}Expected screen and behavior
Initially: Student greeting, Student name field containing Maya, instructions, Create greeting and No greeting yet.
Submitting displays Hello, Maya.Semantics survive the React render
React · the label, field and feedback
import React, { useId, useState } from 'react';
export default function App() {
const nameId = useId();
const helpId = useId();
const [name, setName] = useState('Maya');
const [message, setMessage] = useState('No greeting yet');
function submit(event) {
event.preventDefault();
setMessage(name.trim() ? 'Hello, ' + name.trim() : 'Enter a student name');
}
return <main><h1>Student greeting</h1><form onSubmit={submit}>
<label htmlFor={nameId}>Student name</label>
<input id={nameId} aria-describedby={helpId} value={name}
onChange={event => setName(event.target.value)} />
<p id={helpId}>Use the name shown on the class register.</p>
<button type="submit">Create greeting</button>
</form><p role="status">{message}</p></main>;
}React updates the message text when state changes; the browser still sees a form, labeled input, button and status region. Accessibility comes from those resulting elements and relationships. Component names such as AccessibleField are helpful to developers, but their names alone do not improve the HTML.
role="status" expresses advisory feedback, commonly announced politely by assistive technology. Keep messages short and relevant. Avoid making every small visual update an alert, since constant interruption can make an application harder to use. A critical field error may require a different presentation and focused guidance.
Test the entire task, not one attribute
Start without a mouse. Tab to the input and button, type a name, and submit. Focus should remain visible and move in a sensible order. A custom control that responds only to clicks fails this task even if it has an aria-label. Prefer native elements before attempting to reproduce their keyboard behavior.
Check heading order, zoom and narrow layouts as well. A teacher enlarging text should still find the submit action without losing the field label. Images that convey information need appropriate alternatives; decorative images need a different policy. These checks evaluate the page people use, not just the presence of selected attributes.
Worked practice: explain an empty submission
Start with an empty field and submit. Keep the label and instructions visible, then display an actionable message. The complete worked answer uses the same trimming rule, so whitespace alone does not produce an apparently successful greeting.
Enter a student name appears in the result region. The form does not clear the user’s draft automatically. A larger form should associate errors with the affected fields and decide whether moving focus to an error summary helps users; this small one-field example only shows the message mechanism.
React · JavaScript with JSX
import React, { useId, useState } from 'react';
export default function App() {
const nameId = useId();
const helpId = useId();
const [name, setName] = useState('');
const [message, setMessage] = useState('No greeting yet');
function submit(event) {
event.preventDefault();
setMessage(name.trim() ? 'Hello, ' + name.trim() : 'Enter a student name');
}
return <main><h1>Student greeting</h1><form onSubmit={submit}>
<label htmlFor={nameId}>Student name</label>
<input id={nameId} aria-describedby={helpId} value={name}
onChange={event => setName(event.target.value)} />
<p id={helpId}>Use the name shown on the class register.</p>
<button type="submit">Create greeting</button>
</form><p role="status">{message}</p></main>;
}Expected screen and behavior
Initially: an empty Student name field and No greeting yet.
Submitting displays Enter a student name.ARIA does not repair an unclear interaction
Do not add a button role to a div and assume it now has every native button behavior. Do not remove focus outlines without an equally visible replacement. Disabled controls also need an understandable explanation when users cannot complete a task.
A labeled input is useful evidence, but it is not proof that the whole page works with every assistive technology. Automated checks can reveal selected problems; manual keyboard and screen-reader checks remain valuable. For modal dialogs, additional focus entry, containment, escape and return behavior is required beyond placing a panel on top of the page.
Check the label
Apply native behavior
Build a task people can operate and understand
Use native controls, persistent labels, meaningful instructions and visible result messages. Check keyboard focus and complete tasks in the actual page. React can preserve these relationships as values change, but attributes alone do not establish a usable experience.
Apply these checks to form validation and tests that query controls by their accessible names.
Reaching the recap and completing the quizzes are separate milestones.