How to Make Your Next.js Website Accessible: WCAG 2.1 Compliance for React Developers

WCAG Repair Team

Article title card with a dark green code-brackets icon, representing developer-level fixes

If you're building with Next.js, you're already ahead of the curve on performance and SEO. But Next.js accessibility WCAG compliance is the piece most React developers skip — and it's becoming an expensive mistake. In 2023 alone, over 4,600 ADA-related web accessibility lawsuits were filed in federal court, a number that's been climbing steadily for five years. E-commerce, SaaS, and professional services sites are prime targets. The good news: Next.js has the right hooks to make compliance achievable, and most critical fixes take less than a day to implement.


Why Next.js Creates Unique Accessibility Challenges

React-based frameworks like Next.js introduce accessibility problems that static HTML sites don't have. Client-side routing is the biggest culprit. When a user navigates between pages in a traditional site, the browser announces the new page to screen readers automatically. In Next.js, the DOM updates without a full page reload — meaning screen reader users often have no idea the page has changed.

Other common issues in Next.js projects include:

  • Dynamic content rendered after hydration that isn't announced to assistive technology
  • Custom components (modals, dropdowns, carousels) built without ARIA attributes
  • Images added through next/image without meaningful alt text
  • Focus management that breaks after route changes
  • Color contrast failures hidden inside Tailwind utility classes

These aren't hypothetical edge cases. They're the issues that show up in demand letters.


The WCAG 2.1 Requirements That Actually Get Sites Sued

WCAG 2.1 has three conformance levels: A, AA, and AAA. Courts and regulations (ADA, Section 508, European Accessibility Act) typically require Level AA. Here's where most Next.js sites fail:

1.1.1 Non-text Content (Level A)

Every image needs a descriptive alt attribute. Decorative images need alt="". Next.js's <Image /> component accepts an alt prop — but it doesn't force you to use it meaningfully.

// Bad
<Image src="/hero.jpg" alt="image" width={800} height={400} />

// Good
<Image src="/hero.jpg" alt="Smiling customer using our project management dashboard" width={800} height={400} />

1.4.3 Contrast Ratio (Level AA)

Text must have a 4.5:1 contrast ratio against its background (3:1 for large text). Light gray on white — a design trend that won't die — fails this test constantly. Use the WebAIM Contrast Checker before any color goes into your Tailwind config.

2.4.3 Focus Order (Level A)

Users navigating by keyboard need a logical focus sequence. Next.js's <Link /> component handles some of this, but custom interactive elements — dropdowns built with div tags, modal dialogs, accordions — often trap or lose focus entirely.


Fixing Route Change Announcements in Next.js

This is the fix that makes the biggest difference for screen reader users and is almost never implemented out of the box.

When a route changes, you need to move focus to a meaningful element or announce the new page title. Here's a pattern using Next.js's useRouter and a live region:

// components/RouteAnnouncer.jsx
import { useEffect, useRef } from 'react';
import { useRouter } from 'next/router';

export function RouteAnnouncer() {
  const router = useRouter();
  const announcer = useRef(null);

  useEffect(() => {
    const handleRouteChange = (url) => {
      if (announcer.current) {
        announcer.current.textContent = '';
        setTimeout(() => {
          announcer.current.textContent = `Navigated to ${document.title}`;
        }, 100);
      }
    };

    router.events.on('routeChangeComplete', handleRouteChange);
    return () => router.events.off('routeChangeComplete', handleRouteChange);
  }, [router]);

  return (
    <div
      ref={announcer}
      aria-live="polite"
      aria-atomic="true"
      style={{
        position: 'absolute',
        width: '1px',
        height: '1px',
        overflow: 'hidden',
        clip: 'rect(0,0,0,0)',
        whiteSpace: 'nowrap',
      }}
    />
  );
}

Add <RouteAnnouncer /> to your _app.js layout and screen readers will announce page changes on every navigation event.


Accessible Custom Components: The Patterns You Need

React developers love building custom UI components. WCAG requires they behave like native HTML equivalents.

Buttons vs. Divs

If it does something, it must be a <button>. No exceptions.

// This will get you sued
<div onClick={handleSubmit} className="btn-primary">Submit</div>

// This won't
<button onClick={handleSubmit} className="btn-primary">Submit</button>

Buttons are keyboard focusable, announce their role to screen readers, and respond to Enter/Space by default. Divs do none of this.

Modals need to trap focus while open, return focus when closed, and be dismissible with Escape. Use the focus-trap-react package rather than rolling your own:

import FocusTrap from 'focus-trap-react';

function Modal({ isOpen, onClose, children }) {
  if (!isOpen) return null;

  return (
    <FocusTrap focusTrapOptions={{ onDeactivate: onClose }}>
      <div role="dialog" aria-modal="true" aria-labelledby="modal-title">
        <h2 id="modal-title">Confirm Action</h2>
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </FocusTrap>
  );
}

Automated Testing Won't Catch Everything — But Start There

Tools like axe-core, Lighthouse, and eslint-plugin-jsx-a11y catch roughly 30-40% of accessibility issues automatically. That's still worth doing.

Add eslint-plugin-jsx-a11y to your Next.js project:

npm install eslint-plugin-jsx-a11y --save-dev

Then update .eslintrc.json:

{
  "extends": ["next/core-web-vitals", "plugin:jsx-a11y/recommended"]
}

This catches missing alt attributes, improper ARIA usage, and unlabeled form fields at build time — before they ever reach production.


What to Prioritize First

If you're starting from zero, don't try to fix everything at once. Work through this order:

  1. Add skip navigation links so keyboard users can bypass your header
  2. Fix all images — either meaningful alt text or alt=""
  3. Audit color contrast across your entire design system
  4. Implement the route announcer from the example above
  5. Replace every div and span used as a button with <button>
  6. Add proper labels to all form inputs (htmlFor matching id, or aria-label)
  7. Test with a keyboard — tab through every page, every interactive element

Next.js accessibility WCAG compliance isn't a one-time audit. It's a development practice. Build these checks into your PR process and you'll stop creating new problems while you fix the old ones.


The Department of Justice finalized rules in April 2024 explicitly extending ADA Title II requirements to web content, citing WCAG 2.1 Level AA as the standard. Private sector suits under Title III continue to rise. Settlement costs typically range from $25,000 to $100,000+ when legal fees are factored in — for issues that could have been fixed for a few hours of developer time.

Next.js accessibility WCAG compliance is no longer a nice-to-have. It's risk management.


Scan your site for free at wcagrepair.com and see exactly where you stand. For $8.99, get a full remediation guide with the exact code fixes mapped to your specific failures — no vague recommendations, just the changes you need to make.

Share X LinkedIn Copy link

Ready to scan your site?

Find and fix WCAG accessibility issues with our AI-powered scanner.

Scan Now