Skip to main content
EAAPass
  • Pricing
  • Dashboard
  • Blog
  • About EAA
Blog / React Accessibility: Building WCAG-Compliant React Apps

React Accessibility: Building WCAG-Compliant React Apps

June 27, 2026 · EAAPass Team react javascript wcag frontend accessibility aria

React Accessibility: Building WCAG-Compliant React Apps

React is the dominant JavaScript framework for building web applications in 2026. It's powerful, flexible, and has excellent ecosystem support — but none of that makes your app accessible automatically. Accessibility in React requires deliberate choices at every level of your component hierarchy.

This guide covers the essential patterns, common pitfalls, and practical techniques for building React apps that conform to WCAG 2.1 Level AA.

React's Accessibility Foundations

React itself is accessibility-neutral. JSX renders to HTML, and HTML has accessibility semantics built in through native elements. The problem arises when developers:

  • Use <div> and <span> instead of semantic HTML elements
  • Build custom components without proper ARIA attributes
  • Manage focus incorrectly during state transitions
  • Forget to handle keyboard events alongside mouse events
  • Create dynamic content that isn't announced to screen readers

Essential Principle: Use Semantic HTML First

Before reaching for ARIA, use the correct HTML element. React JSX supports the full HTML element set.

// Bad: div as button
<div onClick={handleClick} className="btn">Submit</div>

// Good: actual button element
<button onClick={handleClick} className="btn">Submit</button>

Native HTML elements like <button>, <a>, <input>, and heading elements (<h1>–<h6>) come with built-in accessibility semantics, keyboard support, and screen reader compatibility. Custom components built from <div> require you to recreate all of that manually.

Accessible React Component Patterns

Form Inputs with Labels

// Always associate labels with inputs
<div>
  <label htmlFor="email">Email address</label>
  <input
    type="email"
    id="email"
    name="email"
    aria-required="true"
    aria-describedby={error ? "email-error" : undefined}
  />
  {error && (
    <span id="email-error" role="alert">{error}</span>
  )}
</div>

Modal Dialogs

Modals are one of the most common accessibility failure points in React apps. A properly accessible modal must:

  • Move focus into the modal when it opens
  • Trap focus within the modal while it's open
  • Return focus to the triggering element when it closes
  • Have a proper role="dialog" and aria-modal="true"
  • Have an accessible name via aria-labelledby
  • Be closeable via the Escape key

Consider using Radix UI Primitives or Headless UI for complex components like modals — these libraries handle accessibility correctly out of the box.

Focus Management After Navigation

// In a React Router app, announce page changes to screen readers
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';

function PageAnnouncer() {
  const location = useLocation();
  const announcerRef = useRef(null);

  useEffect(() => {
    if (announcerRef.current) {
      announcerRef.current.textContent = document.title;
    }
  }, [location]);

  return (
    <div
      ref={announcerRef}
      aria-live="polite"
      aria-atomic="true"
      className="sr-only"
    />
  );
}

Live Regions for Dynamic Content

// Announce dynamic content updates to screen readers
function StatusMessage({ message, type }) {
  return (
    <div
      role="status"
      aria-live={type === 'error' ? 'assertive' : 'polite'}
      aria-atomic="true"
    >
      {message}
    </div>
  );
}

Common React Accessibility Mistakes

1. Event Handlers Without Keyboard Support

// Bad: click only
<div onClick={handleSelect}>Option 1</div>

// Good: keyboard + click
<div
  onClick={handleSelect}
  onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleSelect(); }}
  role="option"
  tabIndex={0}
  aria-selected={isSelected}
>Option 1</div>

2. Missing Image Alt Text

// Bad
<img src={product.image} />

// Good
<img src={product.image} alt={product.name} />

// Decorative image
<img src={decorativeIcon} alt="" aria-hidden="true" />

3. Insufficient Color Contrast

React doesn't help you with color contrast — that's a CSS problem. Ensure all text has at least 4.5:1 contrast against its background (3:1 for large text, 18px+ regular or 14px+ bold).

Testing React Accessibility

Use a multi-pronged testing approach:

  • jest-axe — Integrates axe-core into your Jest test suite for automated component-level testing
  • @testing-library/react — Encourages accessible query patterns (getByRole, getByLabelText)
  • Storybook + a11y addon — Tests components in isolation
  • EAAPass — Tests your deployed application against all 50 WCAG 2.1 AA criteria and provides a compliance report

Recommended Libraries

  • Radix UI — Unstyled, accessible primitives for complex components
  • Headless UI — Accessible component implementations from Tailwind Labs
  • React Aria (Adobe) — Full accessibility hooks library
  • focus-trap-react — Focus trapping for modals

Audit Your React App with EAAPass

No matter how careful you are with individual components, you need to audit the deployed application to catch integration-level issues. EAAPass renders your React app with a full headless browser, executes JavaScript, and then tests the rendered output against WCAG 2.1 AA. You'll catch issues that component-level testing misses.

Run your free audit at eaapass.eu — no signup, results in 60 seconds.

Related resources

  • EAA Compliance in Portugal
  • EAA Compliance in Germany
  • EAA Compliance in France
  • WCAG 1.4.3: Contrast (Minimum)
  • WCAG 4.1.2: Name, Role, Value
  • EAAPass vs AccessiBe
  • Pricing & Plans

Is your website EAA compliant?

The European Accessibility Act is mandatory since June 2025. Run a free WCAG 2.1 AA audit in under 60 seconds — no signup, no credit card.

Run free audit now

12,000+ audits completed. Trusted by businesses across 27 EU countries.

© 2026 EAAPass — Tecnologias Online Lda · NIF 517594340 · Portugal

TechsOn · Zavo · Analyze competitors: BenchSpy.com · info@eaapass.eu

  • Blog
  • Accessibility statement
  • Privacy policy
  • Terms of service