Standards · ARIA

State Live-region state

aria-live

Marks a region whose updates should be announced by assistive technology without moving focus. Pick "polite" for most cases, "assertive" for genuinely urgent updates. The region must be in the DOM at initial render.

When to use

When some part of the page updates asynchronously and you want screen-reader users to know without disrupting whatever they’re currently reading. Form validation summaries, search-result counts, toast notifications, chat-stream messages, and shopping-cart counters are the typical cases.

Choose the politeness level deliberately:

  • aria-live="polite" — wait until the user is idle before announcing. Use this for almost everything. Status messages, results loaded, item added to cart.
  • aria-live="assertive" — interrupt the user immediately. Reserve for genuinely urgent information — session expiring in 30 seconds, form submission failed, payment declined. Overuse makes the page feel hostile.
  • aria-live="off" (the default) — no announcements.

The native roles role="status" (implicit polite) and role="alert" (implicit assertive) bundle aria-live with sensible defaults. Prefer those when they fit; reach for aria-live on a custom container when they don’t.

How to keep it in sync

The crucial rule: the live region must be in the DOM at initial render. Browsers and assistive technologies set up the region’s “watch” when it first appears in the accessibility tree. If you create the region and inject content into it in the same JavaScript tick, the announcement is often missed.

The pattern is:

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

Render the empty container at page load. Later, write text into it with JavaScript. The screen reader announces the change.

Other rules:

  • Update by setting textContent; replacing the entire region’s outer HTML can break the watch.
  • Repeat announcements need a content change — writing the same string twice often produces no second announcement. Add a counter, a timestamp, or briefly clear the region.
  • Pair with aria-busy="true" during multi-step updates to avoid partial announcements.
  • Pair with aria-atomic to control whether the diff or the whole region is announced.

Common failures

  • Creating the live region in the same tick as the content — no announcement.
  • Using aria-live="assertive" for everything. Users mute the tab.
  • Setting aria-live on a focusable control. Live regions are for status updates, not interactive widgets.
  • Hiding the live region with display: none. CSS-hidden regions are also hidden from the accessibility tree and produce no announcement; use the visually-hidden technique (clip / sr-only) instead.
  • Stuffing very long content (paragraphs of text) into a live region all at once — the user can’t skim.
  • Forgetting to clear the region after the message has been read, so subsequent identical updates produce nothing.

Example

<form>
  <label for="zip">ZIP code</label>
  <input id="zip" name="zip" />
  <button type="submit">Look up</button>
</form>

<!-- Always present from initial render -->
<div id="lookup-status" aria-live="polite" class="sr-only"></div>

<script>
  document.querySelector('form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const status = document.getElementById('lookup-status');
    status.textContent = 'Looking up location…';
    const place = await lookup(document.getElementById('zip').value);
    status.textContent = `Location: ${place}`;
  });
</script>