Skip to Content
Component ExamplesLive Regions

Live Regions

Accessible live region patterns for status messages, alerts, update timing, and implementation-neutral assistive-technology expectations

Maintained by Martin KrugerLast reviewed

Overview

Live regions can expose dynamic content changes without moving focus. role="status" has polite live-region semantics; role="alert" has assertive semantics and should be reserved for urgent information. A reliable interoperability technique is to render an empty live-region container first and update its text later. Dynamically inserted alerts are not prohibited, but results vary by role, insertion sequence, browser, assistive technology, and timing.

WCAG Criteria:

  • 4.1.3 Status Messages  — qualifying status information must be programmatically determinable without receiving focus

Key requirements:

  • Use role="status" for non-urgent updates (cart counts, success messages)
  • Use role="alert" for urgent errors and warnings
  • Prefer a rendered, empty live-region container and update its text for broadly compatible behavior
  • Treat dynamic insertion behavior as interoperability evidence, not a universal WCAG rule
  • aria-atomic="true" causes the full region to be re-read on each change (useful for counters)

Status Update (Polite)

Cart Counter with role=status vs. No Live Region

Inaccessible
Cart: 0 items
View inaccessible code
<!-- No live region — screen reader never announces the cart update --> <button onclick=" var el = document.getElementById('bad-cart'); var n = parseInt(el.textContent) + 1; el.textContent = n + ' item' + (n === 1 ? '' : 's'); "> Add to Cart </button> <div id="bad-cart">Cart: 0 items</div>
Accessible
Cart: 0 items
View accessible code
<!-- role="status" announces changes politely without interrupting --> <button onclick=" var count = parseInt(document.getElementById('cart-count').dataset.count || 0) + 1; document.getElementById('cart-count').dataset.count = count; document.getElementById('cart-status').textContent = 'Cart: ' + count + ' item' + (count === 1 ? '' : 's'); "> Add to Cart </button> <!-- Live region exists on page load — content is updated, not the element --> <div id="cart-status" role="status" aria-atomic="true"> Cart: 0 items </div>

Alert (Assertive)

One-Step Dynamic Alert vs. Rendered Empty Alert

Inaccessible

Role and content are inserted together, which has inconsistent support across browser and AT combinations.

View inaccessible code
<!-- Role and content are inserted together; support varies by AT/browser. --> <button onclick=" var err = document.createElement('div'); err.setAttribute('role', 'alert'); err.textContent = 'Session expired. Please log in again.'; document.body.appendChild(err); "> Simulate Error </button>
Accessible
View accessible code
<!-- Empty alert remains rendered; only its text changes. --> <div id="error-banner" role="alert"></div> <button onclick=" var banner = document.getElementById('error-banner'); banner.textContent = 'Session expired. Please log in again.'; "> Simulate Error </button> <!-- To clear: set textContent to ''. -->

Key Concepts Reference

aria-live Values

ValueBehaviorImplicit Role
politeWaits for user to finish current activityrole="status", role="log"
assertiveInterrupts immediatelyrole="alert"
offNo announcements (useful during auto-rotation)

aria-atomic

  • aria-atomic="true" — the entire region is re-read on any change (good for counters: “Cart: 3 items”)
  • aria-atomic="false" (default) — only the changed nodes are read (good for chat logs where only new messages matter)
  • role="status" has aria-atomic="true" by default; role="log" has aria-atomic="false" by default

aria-relevant

Controls which types of changes trigger announcements:

  • additions — new nodes added (default for most live regions)
  • removals — nodes removed
  • text — text content changed
  • all — shorthand for additions removals text

Most of the time, the default (additions text) is correct. Only set aria-relevant explicitly if you need removal announcements.

Throttling Rapid Updates

Rapid live region updates flood the speech queue, making the screen reader unusable. Best practices:

  • Debounce updates — wait 500ms+ after the last change before updating the live region
  • Announce at thresholds — “10 results”, “50 results”, “100 results” instead of every increment
  • Use aria-live="off" during auto-play/auto-scroll, switch to "polite" for manual interaction
  • Never use role="alert" for frequent updates — it interrupts every time

Resources