Toast
A transient message and the live region it lives in. The heaviest component in the library, because announcing something without interrupting the wrong person is genuinely hard.
Example (a11y tier)
Hover one and it stops counting down. So does moving focus into it, and so does switching tabs.
Import
import { Toast, ToastRegion } from 'abaabil/toast' // structure onlyimport { Toast, ToastRegion } from 'abaabil/toast/styled' // + CSSimport { Toast, ToastRegion, ToastLive } from 'abaabil/toast/a11y' // + live regions, timersThere is no toast() function
An imperative API needs a module-level store, a subscription, and a root you must remember to mount. That is a state manager shipped inside a component library, and you already have somewhere to keep a list of things that happened.
So you keep the array and this renders and announces it. Radix's toast works the same way. If the imperative call is what you want, sonner and react-hot-toast do it well in about twenty kilobytes, and shadcn/ui's toast is sonner with a wrapper, which is what its column on the comparison measures.
Mount the region empty and leave it there
A screen reader watches an existing live region for changes. Inserting the region and its first message in the same commit usually announces nothing at all, because there was no region to change.
This is the single most common way toast accessibility breaks, it is invisible unless you test with a screen reader, and it cannot be fixed from inside Toast. So ToastRegion goes near the root of the app, once, and stays mounted whether or not anything is in it.
There are two live regions inside it, not one, because aria-live cannot be changed on an element that already has content queued without the change being ignored. ToastLive renders both; put polite messages in one and assertive ones in the other.
Politeness comes from the variant
Not from the caller, because everything feels urgent to the person writing it. danger is assertive and interrupts whatever is being read; everything else is polite and waits. A stream of polite “Saved” messages is ignorable. A stream of assertive ones makes the page unusable with a screen reader, and that failure is invisible to anyone not using one.
Timers, and WCAG 2.2.1
duration auto-dismisses. The timer pauses while the pointer is over the toast, while focus is inside it, and while the document is hidden, so a message fired in a background tab is still there when you come back rather than having expired unseen.
A toast with an action should have no duration at all. WCAG 2.2.1 asks that timed content can be turned off or extended, and a five-second window to hit “Undo” fails that for most people and all of them under pressure. Pass duration={null} and let the close button dismiss it; the component warns in development if you combine the two.
What it does not do
There is no exit animation. A toast that animates out has to hold a “leaving” state and delay its own unmount, which means the component would own the list rather than you. The entry is CSS; the exit is immediate. That is a real limitation and it is the price of the array staying yours.
The region is fixed-position rather than in the top layer. A toast must not be modal, and both the Popover API and <dialog> put content above everything, including a dialog already open, which would let a toast cover the thing it is commenting on.
Props
| Prop | Type | Default | Tier | Description |
|---|---|---|---|---|
variant | 'neutral' | 'success' | 'warning' | 'danger' | 'neutral' | all | Also sets politeness at the a11y tier. |
duration | number | null | 6000 | a11y | Milliseconds, or null to stay. |
onDismiss | () => void | — | a11y | Remove it from your list here. Omitting it renders no close button. |
closeLabel | string | 'Dismiss' | a11y | Names the close button. |
action | ReactNode | — | a11y | A button or link. Warns if combined with a duration. |
align | 'start' | 'end' | 'end' | all | ToastRegion. |
position | 'top' | 'bottom' | 'bottom' | all | ToastRegion. |
label | string | 'Notifications' | a11y | ToastRegion. Names the landmark. |