Accessibility (a11y) in React
In this page:
<button onClick={handleClick}>Label</button>
<img src="image.png" alt="Description of the image" />
<label htmlFor="fieldId">Label</label>
<input id="fieldId" />
<button aria-label="Close">X</button>
Prefer Semantic HTML Elements
A real <button> element comes with built-in keyboard support (Enter/Space triggers it), correct screen-reader announcements, and proper focus behavior automatically.
A <div onClick={...}> styled to look like a button has none of this by default — you'd have to painstakingly reimplement it all yourself.
Example: Prefer Semantic HTML Elements
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
return (
<div>
<button onClick={() => alert("Clicked")}>Good: real button, keyboard-accessible</button>
<br/>
<div onClick={() => alert("Clicked")} style={{cursor: "pointer", color: "blue"}}>Bad: div pretending to be a button</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Providing Text Alternatives
Every meaningful image needs an alt attribute describing its content for users who can't see it, and icon-only buttons need an accessible label (via aria-label) since there's no visible text for a screen reader to announce.
Example: Providing Text Alternatives
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
return (
<div>
<img src="chart.png" alt="Sales increased 20% in Q3" />
<button aria-label="Close dialog">✕</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Keeping Focus Visible and Logical
Keyboard users rely on a visible focus outline to know which element is currently selected, and expect Tab to move through interactive elements in a sensible, predictable order matching the visual layout.
Removing the focus outline entirely, without a clear replacement, leaves keyboard users with no way to tell where they are.
Example: Keeping Focus Visible and Logical
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
return (
<div>
<button style={{ outline: "2px solid transparent" }}>Bad: focus indicator hidden entirely</button>
<button style={{ outlineOffset: "2px" }}>Good: keeps a visible focus outline</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Using a <div> with an onClick handler instead of a real <button>, losing built-in keyboard support and screen-reader semantics for free.
- Forgetting alt text on meaningful images, leaving screen-reader users with no idea what's shown.
- Removing focus outlines with CSS (outline: none) without providing any visible alternative, making keyboard navigation invisible.
- Accessibility (a11y) means building UI usable by people with a wide range of abilities, including screen-reader and keyboard-only users.
- Semantic HTML elements (button, nav, main) provide built-in accessibility behavior that a generic div can't replicate for free.
- ARIA attributes (aria-label, aria-hidden) fill gaps when semantic HTML alone isn't sufficient.
- Visible focus indicators and logical tab order matter for anyone navigating by keyboard, not just assistive-technology users.
Semantic HTML/ARIA support is universal across modern browsers; no specific React version requirement.
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Error Boundaries
- React Portals
- Modals using Portals (practical use)
- React Suspense
- Code Splitting with React.lazy
- Introduction to Server Components
- Introduction to Next.js (server-side React)
- Using React with TypeScript
- Scalable Folder Architecture
- Common React Design Patterns
- Component Documentation with Storybook
- Accessibility (a11y) in React
- i18n with react-i18next
- React Security Best Practices