← Back to React Course | Chapter 13: Advanced React & Architecture | Lesson 12 of 14

Accessibility (a11y) in React

Accessibility in React is like making sure a building has ramps and elevators, not just stairs, so everyone can actually get inside and use it, no matter their abilities.

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.

Note: Before reaching for a div/span with a click handler, check if there's already a semantic HTML element (button, a, nav, main, header) that fits the purpose.

Warning: A clickable div with no keyboard handling is completely unusable for anyone navigating by keyboard alone, including many screen-reader users.

Example: Prefer Semantic HTML Elements

markup
<!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.

Note: For purely decorative images with no informational content, use alt="" (empty, not omitted) so screen readers correctly skip announcing them.

Warning: An icon-only button with no aria-label announces as just button to a screen reader user, with no indication of what it actually does.

Example: Providing Text Alternatives

markup
<!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.

Note: If a default focus outline doesn't match your design, replace it with an equally visible custom style — don't just remove it.

Warning: outline: none with no visible alternative makes keyboard navigation effectively invisible, a serious accessibility regression.

Example: Keeping Focus Visible and Logical

markup
<!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>
Common Mistakes
  1. Using a <div> with an onClick handler instead of a real <button>, losing built-in keyboard support and screen-reader semantics for free.
  2. Forgetting alt text on meaningful images, leaving screen-reader users with no idea what's shown.
  3. Removing focus outlines with CSS (outline: none) without providing any visible alternative, making keyboard navigation invisible.
Chapter Summary
  • 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.
Browser Support

Semantic HTML/ARIA support is universal across modern browsers; no specific React version requirement.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.