Skip to content
Back to Design Hub

Live Regions for Dynamic Updates

Announcing async state changes (toasts, search results, form errors) to screen readers.

AccessibilityIntermediate

Why

When a toast appears or search results update, sighted users see it instantly. Screen-reader users hear nothing unless the update is in a live region — they're left believing nothing happened.

The WebAIM Million 2024 report found 96.3% of homepages had detectable WCAG failures. ADA-related lawsuits exceeded 4,500 in 2023, making accessibility a legal and ethical necessity, not a nice-to-have.

When

Use live regions for any async update the user needs to know about: toasts, search-as-you-type results, form validation errors, loading→done transitions, cart count changes. Do not treat accessibility as a final-stage checklist — retrofitting is 10x more expensive than building it in.

Avoid relying solely on automated scanners; they catch only ~40% of issues.

How

1. Polite — aria-live="polite" for non-urgent (search results, cart count); announces when user pauses 2. Assertive — aria-live="assertive" for urgent (errors, session expiry); interrupts current speech 3. Atomic — aria-atomic="true" to announce the whole region, not just the changed node 4. Relevant — aria-relevant="additions text" (default) controls what changes announce 5. Pre-declare — put the empty live region in initial HTML, update its content later; dynamically adding aria-live is unreliable 6. Role — role="status" implies polite+atomic; role="alert" implies assertive+atomic 7. Test — NVDA/VoiceOver: trigger an update, verify it's announced without moving focus

Example

<!-- Pre-declared empty live regions in the layout --> <div id="toast-region" role="status" aria-live="polite" aria-atomic="true" class="sr-only"></div> <div id="alert-region" role="alert" aria-live="assertive" aria-atomic="true" class="sr-only"></div> // Toast — write to the polite region function showToast(msg: string) { document.getElementById("toast-region")!.textContent = msg; } showToast("Saved to favorites"); // NVDA: "Saved to favorites" (after current speech finishes) // Search results — announce the count function onResults(items: Item[]) { document.getElementById("toast-region")!.textContent = `${items.length} results available`; } // Form error — assertive, interrupts function showError(msg: string) { document.getElementById("alert-region")!.textContent = msg; } showError("Email is required"); // NVDA: "Email is required" (interrupts)

Common Problems

  • ⚠aria-live added dynamically — screen readers don't pick it up, update is silent
  • ⚠Assertive for everything — interrupts constantly, users can't hear anything else
  • ⚠Moving focus instead of using a live region — disorients users, loses their place

Best Practices

  • ✓Pre-declare live regions in static HTML — update content, not the attribute
  • ✓Default to polite — reserve assertive for true errors that need immediate attention
  • ✓Use role=status / role=alert — they imply the right live+atomic combo, less to remember

FAQ