Personal site to archive accessibility-related learnings, mostly focused on technical implementations.

TL;DR: Don't use aria-busy! It doesn't do what you think it does, and it doesn't even do what the spec says it should.


Accessibility is easy, until it's not. When it's not, it can be extremely frustrating for everyone involved. One of the most frustrating things about accessibility is ARIA, and the fact that there is a lot misunderstanding around exactly what ARIA is responsible for. You can do your best due diligence to read up on the official ARIA spec, or even reference patterns from the official ARIA Authoring Practices, but if you are not actually testing with a combination of browsers and assistive technologies yourself, or getting feedback from people experienced with using assistive technologies, then you are setting yourself up for heartbreak. Shocking to most is the fact that ARIA is not fully supported by all browsers and assistive technologies. What works in one browser/assistive technology combination may not work the same with another. It may not work the way you think it should, or work at all. Even worse, is when something that used to work just stops working as expected. GASP!

There are quite a few examples of this, but the one I want to write about is aria-busy. I've seen aria-busy pop up in code a lot over the years, so let's talk about it!

Common misunderstanding of aria-busy

In almost all cases, it was added to content based only on the name and perceived intention. The incorrect perception is that aria-busy="true" is some kind of status to let people using an assistive technology that a piece of content is... busy, e.g. adding or updating content. Toggling to aria-busy="false" is supposed to let people know that the content is now ready, or done loading. I've witnessed it being added to loading indicators like spinners or skeletons.

I get it, it kind of makes sense based on the name alone. Most people will take their preconceived notion based on the name, multiply it by all the incorrect examples across the internet, and make a well-intentioned mistake.

What it's supposed to do

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

- aria-busy | Accessible Rich Internet Applications (WAI-ARIA) 1.2 w3.org

Based on the spec, updates to content with aria-busy="true" are not exposed. Basically, any content already present and rendered is essentially frozen to updates when aria-busy is set to true. Once all the updates are done and aria-busy is set back to false, all the updates are exposed in one big batch, all at once.

The spec makes no mention of updating people with a status between busy or not, or making any announcements. It basically hides updating content until there are no more updates.

What it actually does

Short answer: Not what you expect!

Testing this, results were so inconsistent across combinations that I had to increase my variations to make sure I was able to catch as much as possible. I tried with a simple div, a live region (aria-live="assertive"), and an element with role="feed" based on the spec's inclusion of these types.

NVDA Results

From my testing, NVDA just did not support aria-busy with the div and feed example at all. It did not announce any content as busy or announce on any updates based on aria-busy.

Concerning were the live region examples. Adding content when aria-busy="true" did not make any announcements, which is to be expected, but toggling to false also did not make any announcements. So, more than just ignoring aria-busy, it appears to have basically broken the live region.

JAWS results

JAWS was a bit of a surprise. It worked as spec'd on a plain div that was focused with tabindex="0", updated content was announced only after aria-busy was toggled to false.

However, the live region example always announced when the content was updated, not when aria-busy was set to false. In other words, aria-busy had no impact on the live region.

JAWS also did not announce any content as busy when aria-busy was set to true.

VoiceOver on macOS Results

VoiceOver with Safari on macOS was a mixed bag. First of all, it functioned very similar to NVDA in the sense that no variations of aria-busy worked on a plain div or a Feed.

Unlike NVDA though, VoiceOver treated the live region properly. That is, it did not announce any dynamic updates until aria-busy was toggled to false. So it worked to spec in this case. EXCEPT when the live region was focused with tabindex="0". In this case, there were 0 announcements.

Interestingly, VoiceOver with Safari on macOs was the only combination that announced content as "busy" when aria-busy="true" but only if it was added/updated after initial rendering.

Mobile browsers

VoiceOver on iOS and TalkBack for Android behaved exactly like NVDA. No real support and appears to break the live region example.

Special thanks to Assistiv Labs, which allows me to easily test Windows screen readers like JAWS and NVDA from my MacBook Pro using only my browser! I highly recommend, and you can get a 6 month trial if you use my special promo code: gerard.cohen

Testing matrix

Here are the variations I tried:

  1. Baseline: No ARIA, just content updates
  2. aria-busy="true" on initial load (not updating to aria-busy="false")
  3. aria-busy="true" on initial load, then updating content and aria-busy="false" at the same time
  4. aria-busy="true" on initial load, updating content after 5 second delay, then updating aria-busy="false" after 4 second delay

With each one of those elements and variations, I tested the content with and without highlighting via virtual cursor, as well focused with tabindex="0". I also tried with tabindex="-1" .

Finally, here are the combinations I tested:

  • VoiceOver on macOS Tahoe 26.1 with Safari Version 26.1 (21622.2.11.11.9)
  • VoiceOver and Safari on iOS 26.1
  • JAWS 2026.2510.251 with Chrome Version 140
  • NVDA 2025.3 with Firefox Version 143
  • Android 16 with Chrome 141.0.7390.70

What it used to do

Up until sometime around April of 2024, JAWS was the only screen reader that actually supported aria-busy="true" the way it is defined in the spec: It would hide content from anyone using JAWS. If there is any content available in an element with aria-busy="true", it is still technically rendered and visible on screen. In that sense, it is very much the same as aria-hidden="true".

Unfortunately, there was a bug that toggling back to aria-busy="false" did not expose the previously busy/hidden content. Since no other assistive technologies supported aria-busy at the time, the decision was made to just drop support for it.

What you could do

Previous guidance to was to pair aria-busy with either aria-hidden or use CSS to apply display:none;. This may have been based on previous JAWS behavior, but considering that aria-busy is meant to only hide updates until after everything is completed, I don't think this guidance applies anymore considering the recent results.

I personally don't think that hiding entire areas of content and just popping them up on people is a good experience. That's why loading indicators like spinners or skeletons are used. When done tastefully, they give everyone feedback that something is happening. They also help reduce excessive layout shifts that can be distracting to some people. It's important that everyone benefits from loading indicators, so here is the simplest example of what you can do.

Using text alternatives to convey busy/loading state

Basically, just add a text alternative to the loading indicator that expresses the loading status. There are different ways of adding text alternatives depending on the type of imagery you use, so use whichever text alternative is appropriate. You can then just replace the loading indicator with the completed content. That's it!

What you probably shouldn't do

Whatever you do, think twice before reaching for a live region or alert to announce when content is loading or done loading. There are very few cases where this is appropriate, and in most cases it's just too noisy and intrusive for people using assistive technologies. In the case of loading indicators like spinners or skeletons, there is a potential for more than one instance of these to be on a page at the same time (F**K YOU SPA'S) so just imagine a chorus of "Loading" and "Done loading" announcements bombarding people.

Conclusion

Based on what I have seen, I would not recommend using aria-busy at this time. The spec is too confusing and implementations across browser and assistive technologies are too wonky at this time. I personally think aria-busy would provide great value if it worked the way we all think it should based on the name. Or, at the very least, the spec should be clarified so that browser and assistive technology vendors can implement one solid experience that we can all count on.