European Accessibility Act

EAA compliance for Next.js: WCAG 2.1 AA checklist & fixes

Next.js gives you all the primitives you need for accessible apps — but accessibility is a code discipline, not a framework setting. The most common Next.js-specific issues are focus loss on client-side route changes, dynamic imports stripping landmarks, and React Server Components that render heading hierarchies inconsistently across nested layouts. The dominant React meta-framework for production sites; chosen by Stripe, Notion, TikTok, OpenAI.

Category
Web framework
Standard
WCAG 2.1 Level AA via EN 301 549
Deadline
28 June 2025 (EU consumer services)
Risk for B2C
High — public-facing, consumer-billed

What the EAA actually requires from a Next.js site

The European Accessibility Act (Directive 2019/882) applies to consumer-facing online services from 28 June 2025. For a Next.js site selling to EU consumers, that means the storefront, checkout, account area, and any embedded payment flow have to meet WCAG 2.1 Level AA via the harmonised standard EN 301 549. Microenterprises with under 10 employees and below €2 million in turnover are exempt for services, but not for products.

Fines vary by member state. Germany caps individual penalties at €100,000; France can fine up to 4% of group turnover; Spain reaches €600,000 for serious or repeated breaches. None of those numbers are theoretical — market surveillance authorities have already started auditing storefronts in Germany and France in the run-up to enforcement.

In practice, the work breaks down into three buckets: theme-level fixes (focus styles, contrast, semantics), interaction-level fixes (carousels, modals, drawers, gallery widgets), and content-level fixes (alt text, headings, descriptive link text). The list below covers the Next.js-specific failure points we see most often during scans.

Top WCAG failures we see on Next.js sites

Across hundreds of Next.js scans, the same handful of issues show up over and over. None of them require ripping the theme apart — most are fixable in a few hours by someone comfortable in the platform's editor or template files.

  • Focus is lost on client-side route changes

    next/link and useRouter().push() do not move focus to the new page's main heading. Screen-reader users hear nothing on navigation.

    2.4.3 Focus Order, 4.1.3 Status Messages — Level A/AA
  • Dynamic imports drop landmarks

    Components loaded via next/dynamic with ssr:false render after hydration; if they contain <main> or <nav>, page structure is briefly missing for assistive tech.

    1.3.1 — Level A
  • next/image without alt props

    Required-by-types but trivially set to empty string; many teams ship "" alt for content images, hiding them from screen readers entirely.

    1.1.1 — Level A
  • Form actions without ARIA live announcements

    Server actions and useActionState successes/failures often update silently. Without role="status", users do not learn the form succeeded.

    4.1.3 — Level AA

Concrete code fixes for Next.js

Below are copy-paste fixes for the most common Next.js issues. They assume you have access to your theme code or the platform's custom-code injection panel. If you cannot edit code directly, share these snippets with whoever maintains the site — every one of them is a ten-minute change.

Move focus to <main> on every route change

TSX
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';

export function FocusOnRouteChange() {
  const pathname = usePathname();
  useEffect(() => {
    const main = document.querySelector('main');
    if (main instanceof HTMLElement) {
      main.setAttribute('tabindex', '-1');
      main.focus();
    }
  }, [pathname]);
  return null;
}

Drop into your root layout. After every navigation, focus lands on <main>, restoring screen-reader context.

Form: announce server-action results

TSX
'use client';
import { useActionState } from 'react';

export function ContactForm({ action }: { action: any }) {
  const [state, formAction] = useActionState(action, { ok: false, msg: '' });
  return (
    <form action={formAction}>
      {/* ... fields ... */}
      <p role="status" aria-live="polite">{state.msg}</p>
    </form>
  );
}

role="status" makes assistive tech announce the action result without stealing focus.

Tools and plugins worth installing first

  • eslint-plugin-jsx-a11y — catches missing alt, label, and role issues at lint time

  • next-axe / @axe-core/react in development to surface runtime issues

  • Certvo SSR scan reports the rendered HTML, not the source — catches hydration-only failures

How to scan a Next.js site without missing anything

Automated scanners catch about 30–40% of WCAG issues; the rest need manual review. The good news is that the 30–40% includes the most expensive issues to remediate after the fact, so an automated scan is the cheapest way to get unstuck. Run one before you change a line of theme code.

  • Scan the production build (next start), not the dev server. Dev injects extra UI.

  • Crawl ISR pages with a fresh cache; first-request HTML can differ from steady state.

Run a free public scan against any Next.js URL right now — no signup, results in 60 seconds.

Frequently asked questions

Does Next.js help or hurt accessibility versus a plain React SPA?

Helps. SSR/SSG produces a meaningful initial HTML payload that crawlers and assistive tech see immediately. SPA frameworks without SSR render a near-empty page until JS loads.

Are Server Components automatically accessible?

Server Components do not change accessibility on their own — the rendered HTML is what matters. The benefit is that you can guarantee server-rendered landmarks before any client JS runs.

Other web framework platforms

Find every accessibility issue on your site in 60 seconds.

Free public scan. No card. AI-generated fixes for every issue we find.