Live Announcer

Imperative screen-reader announcements for things with no visible region of their own — a background save finishing, a filter narrowing a list.

src/components/ui/live-announcer.tsx

About

From the component's own source, where it was written.

Imperative screen-reader announcements.

For things that happen without a visible change a reader would notice: a background save finishing, a filter narrowing a list, a shortcut taking effect. Anything with a visible region of its own should carry aria-live there instead — this is the fallback, not the default.

Two regions, not one. Politeness cannot be changed on a live region after the fact in any reliable way, so polite and assertive each get their own and the message goes to the matching one.

Repeating an identical message is the classic failure here: setting the same text twice is not a mutation, so nothing is announced the second time. The counter appended below forces a change the observer will see.

Install

npx astralyx-ui add live-announcer

Usage

import { LiveAnnouncer, useAnnouncer } from '@/components/ui/live-announcer'

<LiveAnnouncer>
  <App />
</LiveAnnouncer>

const announce = useAnnouncer()
announce('Filter applied, 12 results')

Composer

Change the props and the source below follows.

Announced 0 times — silent on screen.
Props
tsx
const announce = useAnnouncer()
announce('Saved', 'polite')

Examples

Announce

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

LiveAnnouncer

PropTypeDefaultDescription
childrenReactNoderequired—

Hooks

HookSignatureDescription
useAnnouncer() => Announce—

Types

TypeDefinitionDescription
Politeness
'polite' | 'assertive'
Imperative screen-reader announcements.

Behaviour

TopicDetailDescription
announce(message, politeness)(string, 'polite' | 'assertive') => voidDefaults to polite. Assertive interrupts whatever is being read — right for an error, wrong for "maintenance on Sunday".
two regionsinternalPoliteness cannot be changed on a live region after the fact, so each gets its own and the message goes to the matching one.
repeat handlinginternalSetting identical text twice is not a mutation and would not be announced; a zero-width counter forces the change without altering what is spoken.