Skip to main content

Accessibility Testing for SPAs and Dynamic Content

Single-page apps change without reloading, which is exactly where page-by-page scanners stop looking. Test route changes, focus and live updates the way people actually move through the app.

Barriers real visitors hitMonitoring · last 30 daysranked by sessions
BarrierSessions
Serious. Cart drawer opens without moving focus31% (26–36%)
Critical. Card number input has no accessible name12% (9–15%)
Moderate. Search results replaced silently9% (6–12%)
Not evaluated. Promo banner contrastNot enough data

Ranked by measured impact over the last 30 days1,240 sessions sampled

Example output, produced by Accessible on a test site. Not a customer site, and no customer data is shown.

Why are single-page apps hard to test for accessibility?

Because the accessibility of a single-page app is mostly behaviour. When the view changes without a page load, the app itself has to tell assistive technology what happened — by moving focus, updating the title and announcing status messages. A scanner checks the page it loaded; it never sees whether any of that happens after a click.

Why SPAs break traditional scanners

A traditional page load gives assistive technology a clear signal: a new document, a new title, focus at the top. A client-side route change gives none of that for free. Scanning each route by URL loads it fresh, so every route can pass while navigating between them leaves focus stranded and says nothing.

Dynamic updates have the same problem. WCAG 4.1.3 exists so that status messages “can be presented to the user by assistive technologies without receiving focus” (W3C Web Accessibility Initiative, 2026). Whether a message is announced depends on how and when it was inserted — something only visible while the app is being used.

Sign in and searchScreen-reader journey · 7 stepsblocked at 5
  1. Passed. 1 · Sign-in form — labels announced
  2. Passed. 2 · Submit — error summary takes focus
  3. Passed. 3 · Dashboard — heading level 1 announced
  4. Serious. 4 · Search field — placeholder, no label
  5. Critical. 5 · Results — list replaced silently
  6. Not evaluated. 6–7 · not reached

Completed 3 of 7 stepsWCAG 4.1.3 · 3.3.2

Example output, produced by Accessible on a test site. Not a customer site, and no customer data is shown.

What breaks in single-page apps

None of these show up in a scan of a freshly loaded route. All of them show up the first time someone uses the app with a keyboard or screen reader.

  • Route changes nobody is told about

    A link swaps the view without a page load. The browser does not announce a new page, the title may not change, and a screen-reader user hears nothing — or the rest of the old page.

    WCAG 2.4.2 · 4.1.3

  • Focus left behind, or dropped

    After navigation, focus stays on a link that no longer exists or falls back to the page body, so the next Tab starts from the top of the document instead of the new content.

    WCAG 2.4.3

  • Content replaced silently

    Search results, filters, infinite scroll and "load more" update the list with no status message, so there is no way to know anything happened without re-reading the page.

    WCAG 4.1.3

  • Live regions that never speak

    A live region rendered at the same moment as its text, or re-created on every update, is often not announced at all. The region has to exist before the message arrives.

    WCAG 4.1.3

  • Dialogs, drawers and menus

    Components mounted on demand open without moving focus into them, let focus escape behind them, or close without returning focus to the control that opened them.

    WCAG 2.1.2 · 2.4.3

  • Loading states with no words

    A spinner or skeleton says "wait" visually. Without a status message, a screen-reader user cannot tell loading from broken.

    WCAG 4.1.3

Testing focus management and live regions

Focus after every change

For each route change, dialog and in-place update, check three things: where focus lands, whether that place makes sense for what just happened, and where focus returns when a temporary view closes. After navigation, focus belongs at the start of the new content; after a dialog closes, it belongs on the control that opened it. The focus management guide walks through each case.

Announcements for every update

Trigger each update with a screen reader running and listen. Results counts, errors, confirmations and loading states should be announced without stealing focus. When they are not, check that the live region exists before the message is inserted and is not re-rendered with it. The ARIA live region testing guide covers the patterns that fail silently.

How Accessible covers single-page apps

Route changes and in-place updates need something that uses the app, not something that loads it.

Sources

  1. Understanding Success Criterion 4.1.3: Status Messages (opens in a new tab). W3C Web Accessibility Initiative, May 11, 2026.

Frequently asked questions

Why do automated scanners miss accessibility issues in single-page apps?

A scanner loads a URL and checks the page it gets. In a single-page app most of what matters happens after that: route changes without reloads, content swapped in place, dialogs mounted on demand. Focus management and announcements during those changes never exist in a freshly loaded page, so a scan cannot see them.

How should focus be handled after a route change in an SPA?

Move focus to the start of the new content — typically its main heading or container, made focusable with tabindex="-1" — and update the document title, so keyboard and screen-reader users land in the new view and know where they are. Leaving focus on the old link or on the page body is a WCAG 2.4.3 failure.

How do I test ARIA live regions in a single-page app?

Trigger each update — a search, a filter, an add-to-cart — with a screen reader running and listen for the announcement. Check that the live region is present in the page before the update, that it is not re-created each time, and that the message makes sense on its own.

Does Accessible catch focus lost after client-side navigation?

Yes, two ways. Journeys check where focus landed after every click and keystroke while attempting a task. Real user monitoring checks where focus landed after every client-side route change in real visitors’ sessions, and reports the routes where it was lost.

See what happens after the first click.

Run a journey through your app, and monitor focus in real visitors’ sessions after every route change.