RatedWithAI

RatedWithAI

Accessibility scanner

React Accessibility Guide 2026: WCAG Compliance for React & Next.js Apps

Updated June 2026·13 min read·Developer Guide

React's component model makes accessibility both easier and harder than vanilla HTML. Easier because you can encode accessible patterns once and reuse them everywhere. Harder because the abstraction layer hides semantic structure from developers who aren't thinking about it — and single-page app routing breaks browser accessibility behaviors that users rely on.

Quick Summary

  • React doesn't generate inaccessible HTML by default — but many common patterns do
  • Client-side routing (React Router, Next.js App Router) requires explicit focus management
  • axe-core catches ~30–40% of WCAG issues automatically; manual screen reader testing is still required
  • jest-axe and @axe-core/playwright integrate accessibility checks into your existing test suite
  • Headless UI libraries (Radix, Headless UI, shadcn/ui) handle ARIA patterns correctly — roll-your-own components often don't

The Most Common React Accessibility Mistakes

Most React accessibility issues fall into a small set of repeating patterns. The good news: fixing them in your component library fixes them everywhere.

onClick on non-interactive elements

Attaching onClick to a <div> or <span> makes it mouse-clickable but not keyboard-focusable or screen reader-accessible. Use <button> for actions, <a href> for navigation. If you must use a div, add role='button', tabIndex={0}, and onKeyDown handlers — but a <button> is almost always correct.

Critical

Missing focus management after route changes

When React Router or Next.js navigates between pages, focus stays wherever it was. Screen reader users never know a new page loaded. In Next.js App Router, use the built-in scroll-to-top focus behavior. In React Router, manually move focus to the page heading or a skip target after navigation.

Critical

Modal and dialog focus traps

Opening a modal without trapping keyboard focus lets users tab behind it, where they can interact with obscured content. When a modal opens: move focus inside it, trap Tab/Shift+Tab within it, and return focus to the trigger element when it closes. Libraries like Radix Dialog and Headless UI Dialog handle this correctly.

High

Dynamic content without ARIA live regions

React's reactive updates don't announce changes to screen readers. Loading states, success toasts, form validation errors, and live search results need aria-live='polite' (or 'assertive' for urgent alerts) on a region element to be announced.

High

Images without alt text

Next.js <Image> and React <img> don't require alt text — you have to add it. For decorative images, use alt='' (empty string). For informative images, describe the content. For images inside <button> or <a>, the alt text becomes the control's accessible name.

High

Custom select/dropdown components

Building a styled select from scratch with onClick handlers almost always produces an inaccessible result. Native <select> is accessible by default. For custom styled dropdowns, implement the Combobox or Listbox ARIA patterns — or use Radix Select/Combobox which implements them for you.

Moderate

Color contrast in Tailwind dark themes

Tailwind's slate/gray palette is beautiful but tricky. slate-400 text on slate-900 background passes. slate-500 on slate-800 is borderline. Always verify contrast ratios — don't assume dark themes are accessible by default.

Moderate

Automated Testing with axe-core

axe-core is the open-source accessibility rules engine behind Deque Axe DevTools, the Chrome axe extension, and most accessibility testing tools. Integrating it into your test suite catches violations at commit time — before they reach production.

jest-axe (Unit / Component Tests)

Install

npm install --save-dev jest-axe @testing-library/react

Usage

import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('Button is accessible', async () => {
  const { container } = render(<MyButton label="Submit" />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Catches ~30–40% of WCAG 2.1 AA issues. Best for component-level regression testing.

@axe-core/playwright (End-to-End Tests)

Install

npm install --save-dev @axe-core/playwright

Usage

import { checkA11y } from '@axe-core/playwright';

test('Homepage passes axe', async ({ page }) => {
  await page.goto('/');
  await checkA11y(page, undefined, {
    detailedReport: true,
    detailedReportOptions: { html: true },
  });
});

Runs against fully rendered pages including dynamic content. Better coverage than component tests alone.

Accessible Component Patterns

The right component library choice determines whether accessibility is easy or a constant battle. Here's how major options compare:

Radix UI / shadcn/ui

Radix implements WAI-ARIA design patterns correctly for every component: Dialog, Select, Tabs, Tooltip, Menu, and more. shadcn/ui wraps Radix with Tailwind styling. If you're starting a new project, this is the right choice.

Best for accessibility

Headless UI (Tailwind Labs)

Accessible, unstyled components for Dialog, Listbox, Combobox, Menu, Tabs, and Disclosure. Integrates naturally with Tailwind CSS. Fewer primitives than Radix but excellent quality on the components it covers.

Excellent

React Aria (Adobe)

The most thorough accessibility implementation available, including internationalization and cross-platform behavior. Steeper learning curve and more boilerplate. Worth it for products with strict accessibility requirements (government, healthcare, enterprise).

Best-in-class, more complex

Material UI (MUI)

Generally accessible but has known gaps in complex components (DataGrid, Date Pickers). Test thoroughly. Accessibility varies by component and version — don't assume coverage.

Good with caveats

Ant Design

Accessibility has improved significantly in v5 but complex components (Table, Select, Tree) still have keyboard and ARIA issues. Requires custom testing and patching for WCAG AA compliance.

Improving but inconsistent

Custom components (no library)

Rolling your own dropdowns, modals, tabs, and tooltips almost always produces accessibility issues. ARIA design patterns are complex — each pattern has keyboard interaction requirements, focus management rules, and state announcements that are easy to miss.

High risk

Next.js-Specific Accessibility Notes

Next.js has built-in accessibility features, but several require explicit opt-in or configuration:

App Router focus management

Next.js App Router automatically announces route changes to screen readers and scrolls to the top on navigation — an improvement over Pages Router. Verify it works with your layout structure; nested layouts that don't reset scroll can interfere.

next/link and focus

Next.js <Link> renders an <a> tag — no accessibility issues. But programmatic navigation via router.push() requires manual focus management. Move focus to the page heading or a designated focus target after programmatic navigation.

eslint-plugin-jsx-a11y

Next.js 11+ ships with eslint-plugin-jsx-a11y configured by default. It catches missing alt text, invalid ARIA attributes, and non-interactive element click handlers at lint time. Check your .eslintrc to ensure it's enabled — it's off in some custom configurations.

next/image alt text

next/image requires an alt prop but accepts alt='' (empty string) for decorative images. The TypeScript types don't enforce meaningful alt text — add lint rules or code review processes to catch placeholder or missing descriptions.

Dynamic imports and lazy loading

Components loaded with next/dynamic can cause accessibility issues if they introduce interactive elements without focus management. Ensure dynamically loaded modals, drawers, and tooltips manage focus correctly on mount.

CI/CD Integration

Automated accessibility checks in your CI pipeline prevent regressions from reaching production. A GitHub Actions workflow that runs axe on every PR costs nothing and catches ~30% of WCAG violations before review.

GitHub Actions — axe-core via Playwright

name: Accessibility Tests
on: [pull_request]
jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps chromium
      - run: npx playwright test --project=accessibility

The axe-core library catches violations with zero false positives — any failure it reports is a real accessibility issue. It does not catch everything: color contrast issues in dynamic states, focus order problems, and screen reader announcement quality all require manual testing. Use automated CI coverage as a floor, not a ceiling.

Manual Screen Reader Testing for React Apps

NVDA + Chrome (Windows)

Most common combination used by screen reader users in the US. Test keyboard navigation, modal behavior, form validation, and live region announcements.

Free

VoiceOver + Safari (macOS/iOS)

Built into macOS and iOS. Important for Apple ecosystem users. VoiceOver on iOS with Safari is the most-used screen reader on mobile.

Built-in (free)

JAWS + Chrome (Windows)

Most common in enterprise and government contexts. Required for Section 508 compliance documentation. More expensive but required for federal contract work.

Paid ($1,000+/year)

Ongoing Monitoring for React Apps

Automated testing in CI catches issues before deployment. Ongoing monitoring catches regressions in production — from content updates, A/B tests, or third-party script changes. React SPAs are particularly susceptible because route-level changes don't trigger full page reloads that some monitoring tools depend on.

RatedWithAI

Automated WCAG scanning with continuous monitoring, single-page app support, exportable reports. Free scan available.

Deque Axe Monitor

Enterprise-grade axe-core-powered monitoring. Integrates with Jira, GitHub, and Azure DevOps for issue tracking workflows.

Siteimprove

Full accessibility, quality, and analytics platform. Strong on enterprise workflows and prioritization dashboards.

Pope Tech

axe-core powered, strong team collaboration features. Popular in higher education and government agencies.

Scan Your React App for Free

RatedWithAI scans against WCAG 2.1 AA criteria and generates exportable reports for your React or Next.js app. No signup required for the initial scan.

Sponsored

Find accessibility issues at the component level

Deque Axe DevTools Pro integrates with your React dev environment — catch issues in the browser as you build, before they hit your test suite.

Try Free →

Related Guides