ESC

Type to search the knowledge base.

aria-live Regions

Announce dynamic updates with aria-live polite/assertive, role=status/alert, and patterns for toasts, validation, and async results.

intermediate3 min read
  • accessibility
  • aria-live

When content updates without a page load — toasts, validation, live search counts — screen reader users may hear nothing unless you use a live region. aria-live tells AT to announce changes in a container.

Docs: ARIA live regions, MDN aria-live.

politeness levels

Value Use
off (default) No announcement
polite Announce when idle — most UI updates
assertive Interrupt — critical errors only
<div aria-live="polite" aria-atomic="true" class="visually-hidden" id="status"></div>
document.getElementById('status').textContent = '3 results found';

Roles that imply live regions

Role Default live behavior
status polite
alert assertive
log polite
timer off (usually)
<p role="status">Saved</p>
<p role="alert">Payment failed</p>

Prefer these roles when they match meaning — cleaner than raw aria-live alone.

atomic and relevant

  • aria-atomic="true" — read the whole region on change, not only the diff
  • aria-relevant — which changes matter (additions, text, …); default is usually fine

Patterns

Toast

<div role="status" aria-live="polite" id="toast"></div>

Keep messages short. Don’t assertive-spam.

Form error summary

<div role="alert" tabindex="-1" id="errors">…</div>

Also move focus for WCAG error identification strategies — accessible forms.

Loading

<div role="status" aria-live="polite">Loading results…</div>

Clear or update when done.

React caveats

Live regions should exist in the DOM before content changes. Mounting a new assertive region with text in one commit can miss announcements. Pattern:

  1. Render empty live region always.
  2. Then set text content.
function LiveAnnouncer({ message }: { message: string }) {
  return (
    <div role="status" aria-live="polite" className="sr-only">
      {message}
    </div>
  );
}

Sometimes a double requestAnimationFrame or clearing then setting text improves reliability across SR/browser pairs.

Footguns

  1. Assertive for every keystroke.
  2. Live region not present before update.
  3. Announcing huge HTML blobs.
  4. Duplicate announcements (status + focus + alert).
  5. Hiding with display:none in ways that remove from a11y tree incorrectly — use visually-hidden CSS that stays “visible” to AT.

Interview out-loud answer

“aria-live announces dynamic updates. polite for most status text, assertive only for critical alerts. role=status/alert are convenient defaults. The region should exist first, then get new text; keep messages short.”

Debouncing announcements

Search-as-you-type can fire live updates every keystroke — unusable. Debounce status text (e.g. 300–500ms) and announce once: “24 results for ‘cat’.” Clear the region when the user navigates away so stale messages don’t replay.

Extra practice

Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.

Notes from real codebases

Teams that succeed here keep the rules mechanical: lint where possible, CI for the rest, and a short human checklist for what automation cannot see. Document exceptions with an owner name and a removal date so “temporary” escapes do not become permanent architecture.

Further reading

Related guides