Skip to main content
CodeRocket
Accessibilityhigharia

Announce dynamic content with ARIA live regions

rule · aria-live-regions

ARIA live regions announce dynamic content changes to screen readers without requiring focus to move.

Code Example

HTML
<!-- Polite: Announced after current speech finishes -->
<div aria-live="polite" id="status">
  <!-- Content inserted here will be announced -->
</div>
 
<!-- Assertive: Interrupts current speech immediately -->
<div aria-live="assertive" id="error">
  <!-- Critical messages only -->
</div>

Why It Matters

Without live regions, screen reader users miss dynamic updates entirely—they won't know their form submitted, their cart updated, or an error occurred.

Politeness Levels

LevelUse ForExample
politeNon-urgent updatesCart count, search results
assertiveCritical alertsErrors, session expiry
offDisable announcementsFrequently updating content

Semantic Live Regions

HTML
<!-- role="alert" implies aria-live="assertive" -->
<div role="alert">
  Your session will expire in 2 minutes
</div>
 
<!-- role="status" implies aria-live="polite" -->
<div role="status">
  3 items added to cart
</div>

Common Use Cases

Form Validation Errors

TSX
function FormField({ label, error }) {
  const errorId = `${label}-error`
 
  return (
    <div>
      <label>{label}</label>
      <input aria-describedby={error ? errorId : undefined} />
      {/* Live region for errors */}
      <div role="alert" id={errorId}>
        {error}
      </div>
    </div>
  )
}

Loading States

TSX
function SearchResults({ isLoading, results }) {
  return (
    <div>
      {/* Status announcements */}
      <div role="status" aria-live="polite">
        {isLoading && 'Loading results...'}
        {!isLoading && `${results.length} results found`}
      </div>
 
      <ul>
        {results.map(item => <li key={item.id}>{item.name}</li>)}
      </ul>
    </div>
  )
}

Toast Notifications

TSX
function Toast({ message, type }) {
  return (
    <div
      role={type === 'error' ? 'alert' : 'status'}
      aria-live={type === 'error' ? 'assertive' : 'polite'}
      className="toast"
    >
      {message}
    </div>
  )
}

Cart Updates

TSX
function CartIcon({ count }) {
  return (
    <div>
      <button aria-label={`Cart, ${count} items`}>
        <ShoppingCartIcon />
        <span>{count}</span>
      </button>
      {/* Announce changes */}
      <div role="status" className="sr-only">
        {count} items in cart
      </div>
    </div>
  )
}

Important Rules

HTML
<!-- ❌ Bad: Live region added with content -->
<div aria-live="polite">New message!</div>
 
<!-- ✅ Good: Live region exists, content added later -->
<div aria-live="polite" id="messages"></div>
<script>
  // Content change triggers announcement
  document.getElementById('messages').textContent = 'New message!'
</script>

Exceptions

  • Prefer native HTML semantics over ARIA when both are possible; some apparent ARIA failures disappear when the underlying element is corrected.
  • A missing ARIA attribute is not automatically the strongest finding if the control is already semantically broken, unnamed, or keyboard-inaccessible.
  • Do not add ARIA only to satisfy the rule if the feature should instead be implemented with a native element or a simpler interaction pattern.

Verification

Automated Checks

  • Use browser accessibility tooling, axe, Lighthouse, or equivalent automated checks against a representative rendered state.

Manual Checks

  • Enable screen reader (NVDA, VoiceOver)
  • Trigger dynamic content updates
  • Verify announcements occur at appropriate times
  • Check that assertive regions don't interrupt unnecessarily