Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTELive Region Attributes

aria-busy

Indicates an element is being modified and that assistive technologies may want to wait until the modifications are complete before exposing the changes to the user.

Attribute Type
Live Region
Value Type
true | false
Default Value
false

Overview

The aria-busy attribute tells assistive technologies that an element is currently being updated. When set to true, screen readers should wait before announcing changes.

This is particularly useful when making multiple rapid changes to a live region. Set it to true before starting updates, then back to false when complete. The screen reader will then announce the final result once.

Why Use aria-busy?

Without aria-busy, screen readers might announce each intermediate change during a complex update. This creates a flood of announcements. Setting aria-busy="true" tells them to wait for the final result.

Live Demo: aria-busy Behavior

Initial content

Current aria-busy state: false

Screen readers will announce the content

Attribute Values

true

The element and its subtree are currently being updated. Assistive technologies should wait before announcing changes. Use this during async operations, batch updates, or DOM manipulations.

false(default)

The element is not currently being updated. This is the normal state. Assistive technologies will announce changes as they occur. This is the default when the attribute is omitted.

Code Examples

Basic Pattern

<!-- Basic aria-busy Usage -->
<div id="content-area" 
     role="region" 
     aria-live="polite"
     aria-busy="false">
  <p>Content will appear here</p>
</div>

<script>
  function loadContent() {
    const container = document.getElementById('content-area');
    
    // Set busy to true before loading
    container.setAttribute('aria-busy', 'true');
    
    // Fetch data
    fetch('/api/data')
      .then(response => response.json())
      .then(data => {
        // Update content
        container.innerHTML = data.html;
        
        // Set busy to false when complete
        container.setAttribute('aria-busy', 'false');
        
        // Screen reader will now announce the new content
      });
  }
</script>

Batch Updates

<!-- Multiple Sequential Updates -->
<div id="feed" 
     role="feed" 
     aria-live="polite"
     aria-busy="false">
  <article>Post 1</article>
  <article>Post 2</article>
</div>

<script>
  async function loadMorePosts() {
    const feed = document.getElementById('feed');
    
    // Start batch update
    feed.setAttribute('aria-busy', 'true');
    
    // Load multiple posts
    const posts = await fetchPosts();
    
    // Add all posts
    posts.forEach(post => {
      const article = document.createElement('article');
      article.textContent = post.title;
      feed.appendChild(article);
    });
    
    // Finish batch update
    feed.setAttribute('aria-busy', 'false');
    
    // Screen reader announces: "5 new articles added" 
    // (instead of announcing each one individually)
  }
</script>

React Examples

// React Component with aria-busy
import { useState } from 'react';

function DataTable({ dataUrl }) {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  
  const loadData = async () => {
    setIsLoading(true);
    
    try {
      const response = await fetch(dataUrl);
      const newData = await response.json();
      setData(newData);
    } catch (error) {
      console.error('Failed to load:', error);
    } finally {
      setIsLoading(false);
    }
  };
  
  return (
    <div
      role="region"
      aria-live="polite"
      aria-busy={isLoading}
      aria-label="Data Table"
    >
      {isLoading ? (
        <div className="loading">
          <span className="spinner" aria-hidden="true"></span>
          <span>Loading data...</span>
        </div>
      ) : (
        <table>
          <thead>
            <tr>
              <th>Name</th>
              <th>Value</th>
            </tr>
          </thead>
          <tbody>
            {data.map(item => (
              <tr key={item.id}>
                <td>{item.name}</td>
                <td>{item.value}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
      
      <button onClick={loadData} disabled={isLoading}>
        {isLoading ? 'Loading...' : 'Refresh Data'}
      </button>
    </div>
  );
}

Best Practices

Set aria-busy="true" BEFORE making changes to the live region

Set aria-busy="false" AFTER all changes are complete

Use for batch updates or multiple sequential DOM changes

Combine with aria-live to create effective loading states

Always reset to false - forgetting breaks future announcements

Use for async data loading that replaces region content

×

Don't leave aria-busy="true" indefinitely

×

Don't use for single, simple updates that don't need batching

×

Don't forget to set it back to false - critical!

×

Don't use as a general "loading" indicator without live regions

Accessibility Notes

Prevents Announcement Spam

When making rapid or multiple changes to a live region, aria-busy="true" prevents screen readers from interrupting the user with each individual change. Set it to false when done, and the final state will be announced once.

Critical: Always Reset

Always remember to set aria-busy back to false! Forgetting to do this will prevent all future announcements from the live region. Use try/finally blocks or cleanup functions to ensure it's reset even if errors occur.

Browser Behavior

Different screen readers may handle aria-busy slightly differently. Some may queue announcements, while others may discard them entirely until busy is false. Test across platforms to ensure consistent behavior.

Related Attributes

Specifications & Resources