Lesson 60 of 80
A screen reader only announces a live region's content when the region's existing DOM node changes — not when React mounts a brand new one that happens to contain the same text.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Most accessibility work is about the DOM you render once. Live regions are about the DOM that changes after the page has already loaded and been read — a save confirmation, a form error, a toast, a "3 new messages" counter. Sighted users see these updates happen; a screen reader user needs to be told, since nothing moved focus and there was no click to react to.
aria-live marks an element as one a screen reader should watch. aria-live="polite" announces the update after the user's current activity finishes — the right choice for most status updates. aria-live="assertive" interrupts immediately — reserve it for things that genuinely can't wait, like a session-expiry warning.
function SaveStatus({ status }) {
return (
<div aria-live="polite" role="status">
{status === "saving" && "Saving..."}
{status === "saved" && "Saved"}
{status === "error" && "Failed to save — try again"}
</div>
);
}
This looks correct and often isn't, for a subtle reason: screen readers detect live regions by watching a DOM node that already exists in the accessibility tree for content mutations inside it. If SaveStatus (or its parent) only mounts this div conditionally — appearing for the first time right when status becomes "saved" — there's no prior mutation to observe. The screen reader sees a new node appear with text already in it, not a change to a node it was watching, and many announce nothing at all.
The fix is to keep the live region's container mounted at all times, even when empty, and change only its text content in response to state:
function SaveStatus({ status }) {
const message =
status === "saving" ? "Saving..." : status === "saved" ? "Saved" : status === "error" ? "Failed to save — try again" : "";
return (
<div aria-live="polite" role="status" className="sr-only-if-empty">
{message}
</div>
);
}
Now div is always present in the tree from first render; only its text content mutates, which is exactly the signal screen readers are watching for.
{status && <div aria-live="polite">{status}</div>} mounts a brand-new node with the text already in it — keep the container always mounted and update only its text.aria-live="assertive" is not "more accessible" — it's more disruptive. It cuts off whatever the screen reader was already announcing, mid-sentence if necessary. Using it for routine status updates (like every autosave) trains users to associate live regions with interruption and annoyance.role="status" and role="alert" already imply a live-region politeness setting (polite and assertive respectively) — pairing role="alert" with an explicit aria-live="polite" is contradictory and behavior across screen readers becomes inconsistent. Pick one mechanism, not both.The accessibility lesson covers getting the static, first-render markup right — labels, roles, focus order. Live regions are the dynamic half of the same problem: once the page is already loaded and being read, React's job shifts from "render correct markup" to "mutate an already-observed node," and that distinction is exactly what determines whether a screen reader notices the change at all.