Skip to Content
Component ExamplesLive Regions

Overview

Live regions allow screen readers to announce dynamic content changes without the user having to move focus. Two levels exist: role="status" (polite — waits for the user to finish what they’re doing) and role="alert" (assertive — interrupts immediately). The most critical rule: the live region element must exist in the DOM before content is injected into it.

WCAG Criteria:

Key requirements:

  • Use role="status" for non-urgent updates (cart counts, success messages)
  • Use role="alert" for urgent errors and warnings
  • The live region element must be present in the DOM on page load
  • Do not use aria-live on dynamically created elements — it won’t work
  • 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
<!-- 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>
Live Preview
Cart: 0 items
Accessible
<!-- 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>
Live Preview
Cart: 0 items

Alert (Assertive)

Persistent Alert Region vs. Dynamically Created Error

Inaccessible
<!-- Dynamically created element — live region property not recognized --> <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>
Live Preview

The dynamically created alert element will not be announced by most screen readers.

Accessible
<!-- Alert container exists in DOM from page load — only content changes --> <div id="error-banner" role="alert" hidden></div> <button onclick=" var banner = document.getElementById('error-banner'); banner.textContent = 'Session expired. Please log in again.'; banner.removeAttribute('hidden'); "> Simulate Error </button> <!-- To clear: set textContent to '' and re-hide -->
Live Preview

Resources