You click a button, and a loading spinner appears on screen. If you can see the spinner, you know to wait. But if you’re using a screen reader, you hear nothing. Did the button work? Should you click again? Without auditory feedback, users end up submitting forms twice or triggering actions multiple times because they have no idea when the loading started.

Every loading state has three moments to communicate: loading started, loading is in progress, and loading completed. Sighted users get visual feedback for all three phases. Screen reader users need auditory announcements to communicate the state changes.

For buttons that trigger loading

When a user clicks a button that starts an async operation, the button needs to indicate its loading state both visually and programmatically. To do this, use aria-disabled="true" during loading, not the disabled attribute.

The disabled attribute removes the button from keyboard navigation entirely. Users tabbing through the page will skip right over it, which means they can’t find the button they just clicked. With aria-disabled, the button stays focusable and announces its disabled state to screen readers.

<button 
  aria-disabled={isLoading}
  aria-label={isLoading ? "Submitting..." : "Submit"}
  onClick={handleSubmit}>
  Submit
</button>

You’ll need to prevent the button action with JavaScript since aria-disabled only affects screen reader announcements, not actual functionality.

For content areas that update

When content loads into a section of the page, use aria-live="polite" on the container. This creates a live region that announces changes to screen readers.

<div aria-live="polite" aria-atomic="true">
  {isLoading ? "Loading content..." : "Content loaded"}
</div>

The aria-atomic="true" tells screen readers to announce the entire region, not just what changed. Without it, screen readers might only announce partial updates, which can be confusing.

The live region must exist in the DOM before you update it. Screen readers need a moment to recognize that the region exists. If you’re adding it dynamically, wait at least two seconds before injecting content into it.

A note on skeleton screens

Skeleton screens are popular visual placeholders during loading. They show gray boxes where content will appear. But they’re purely decorative and should be hidden from screen readers with aria-hidden="true". Pair them with a live region announcement so screen reader users know loading is happening.

Loading states need both visual and auditory feedback. One without the other leaves users guessing whether their action worked or if they need to try again.

Don’t Miss Out

Get practical accessibility tips in your inbox every week.

Matt Litzinger headshot

Matt Litzinger

Matt is a web developer who builds tools that help organizations better engage with customers and improve website accessibility.