Components · Status and feedback
Toast
Transient notifications from useToast(), rendered by ToastProvider.
Success with undo
import { Button, ToastProvider, useToast } from '@rookcom/monarch-react';
function ArchiveButton() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: 'Ticket archived',
description: 'You can undo this.',
tone: 'success',
action: { label: 'Undo', onClick: () => {} },
})
}
>
Archive ticket
</Button>
);
}
<ToastProvider>
<ArchiveButton />
</ToastProvider>Usage examples
Errors from an API
import { Button, ToastProvider, getErrorMessage, useToast } from '@rookcom/monarch-react';
function ErrorButton() {
const { toast } = useToast();
return (
<Button
variant="danger-subtle"
onClick={() =>
toast.error(
getErrorMessage(
{ response: { data: { message: 'That handle is already in use' } } },
{ fallback: 'Could not save the workspace' },
),
)
}
>
Save with a conflict
</Button>
);
}
<ToastProvider label="Error example notifications" position="bottom-center">
<ErrorButton />
</ToastProvider>More time for people who need it
import { Button, ToastProvider, useToast } from '@rookcom/monarch-react';
function ArchiveButton() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: 'Ticket archived',
description: 'You can undo this.',
tone: 'success',
action: { label: 'Undo', onClick: () => {} },
})
}
>
Archive ticket
</Button>
);
}
<ToastProvider
durationScale={2}
label="Timing example notifications"
position="top-right"
>
<ArchiveButton />
</ToastProvider>Purpose
Brief, transient feedback about an action that just happened, with one optional action such as Undo.
Use when
- Confirming an action (saved, archived, assigned).
- Reporting a failure that does not block the page (
toast.error). - Offering Undo for a reversible action instead of asking for confirmation first.
Avoid when
- The message must stay visible: use Alert, InlineNotice or CalloutBanner.
- The user must decide something: use Dialog.
- The error belongs to a field: use the Field error.
Design principles
- Mount ToastProvider once near the root; call
toast()fromuseToast()anywhere below it. - A toast never takes focus; it is announced through a live region and reachable with F8.
- Danger is announced assertively, everything else politely.
- Toasts with an action, and danger toasts, stay 10s by default; others 5s. Users can be given more time with
durationScale. - At most three are open (
limit); older ones close first. - Never put essential information only in a toast. Errors that block the user belong on the page as well.
API reference
import { ToastProvider, getErrorMessage, useToast } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
ToastProvider | component | client | .mn-duration-base |
getErrorMessage | utility | client | – |
useToast | hook | client | – |
toast(options) returns { id, dismiss }. toast.success, toast.error (danger), toast.warning and toast.info take (title, options?).
dismiss(id) closes one toast; dismiss() closes all.
useToast() throws outside ToastProvider.
getErrorMessage(error, { fallback, network }) turns any thrown value into one readable sentence: field errors, message, errors[], detail, from the response body first. Transport messages ("Request failed with status code 500", "Failed to fetch") and HTML error pages are replaced by the fallback.
Migrating from sonner: toast.success(msg, { action }) and toast.error(msg) have the same shape; action is { label, onClick }.
Generated from the TypeScript declarations of packages/react/src/components/feedback/toast.
Properties
ToastOptions: title, description, tone, action ({ label, onClick, altText }), duration, politeness, onDismiss.
ToastProvider
Mount once near the root. Provides `useToast()` and renders the viewport. Toasts never take focus. They pause while hovered or focused, can be reached with F8 (the `hotkey`), and close with Escape. Each one is announced once through a live region: assertively for `danger`, politely otherwise.
| Prop | Type | Description |
|---|---|---|
children required | ReactNode | |
dismissLabel | string | Accessible name of each toast's close button. Default "Dismiss". |
durationScale | number | Multiplies every auto-dismiss duration (WCAG 2.2.1 Timing Adjustable). Wire it to a user preference: `2` doubles the time, `Infinity` turns auto-dismiss off. Default 1. |
hotkey | string[] | Keys that move focus to the toast region, as `KeyboardEvent.code` or modifier names (`['altKey', 'KeyT']`). Default `['F8']`. The hint is added to the region's name ("Notifications (F8)"). |
label | string | Accessible name of the toast region, also read before each announcement. |
limit | number | How many toasts are shown at once; older ones close first. Default 3. |
position | "bottom-right" | "bottom-center" | "top-right" | Where toasts appear. |
Variants
| Prop | Values | Description |
|---|---|---|
tone | 'neutral''info''success''warning''danger' | Accent edge and icon (none for neutral). Default neutral. Exposed as data-tone. |
politeness | 'polite''assertive' | How it is announced. Default assertive for danger, polite otherwise. Exposed as data-politeness. |
ToastProvider position | 'bottom-right''bottom-center''top-right' | Viewport placement. Default bottom-right. Swipe direction is right for right positions, down otherwise. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Open | data-state="open" | Visible; auto-dismisses after its duration unless paused. |
| Paused | – | While the pointer is over the region, focus is inside it, or the window is in the background. |
| Closed | data-state="closed" | Exit animation runs, then the record is removed; onDismiss fires once. |
| Swiping | data-swipe | Follows the pointer and dismisses at the end of a swipe. |
Interaction behaviour
- Auto-dismisses after
duration(5s; 10s for danger or with an action) times the provider’sdurationScale.Infinityon either keeps it until dismissed. - Pauses on hover and focus, and resumes when both leave.
- Activating the action runs
onClickand dismisses the toast. - The Dismiss button, Escape and a swipe close the toast.
Keyboard support
| Keys | Action |
|---|---|
| F8 | Moves focus to the toast region (configurable with hotkey). |
| Tab / Shift+Tab | Moves between toasts, their action and Dismiss buttons. |
| Escape | Dismisses the focused toast. |
Focus is never moved to a toast automatically.
Accessibility
- Role
- region (named, with the hotkey) containing a list; each toast is a list item
- Accessible name
- The region is named by
labelplus the hotkey ("Notifications (F8)"); each toast by its title.
- Each toast is announced once through a separate live region:
aria-live="assertive"for danger (orpoliteness="assertive"),politeotherwise. The list items carry no live role, so nothing is announced twice and the list stays valid. - Toasts never steal focus (WCAG 2.4.3); F8 reaches them, so the action is keyboard accessible (2.1.1).
- Timing is adjustable (WCAG 2.2.1): hover or focus pauses,
durationScalegives more time or turns auto-dismiss off. - The close button is labelled
dismissLabel(default "Dismiss") and described by the toast title, so several toasts stay distinguishable. - The action announces
altText(default its label) so screen-reader users know how else to do it after the toast is gone. - Icons are
aria-hidden. Reduced motion shortens the enter and exit animations to nothing.
Responsive behaviour
- The viewport is min(24rem, 100vw - 2rem) wide, so toasts fit narrow screens down to 320px.
Theme support
- Raised surface and tone accent tokens, so light, dark and high-contrast themes apply.
- Reduced motion shortens the enter and exit animations.
Do and don't
Do
- Keep the description to one line.
- Offer Undo for reversible actions instead of a confirmation dialog.
- Pass errors through
getErrorMessageso people never see raw server or transport text. - Wire
durationScaleto an accessibility preference if your app has one.
Don't
- Do not mount more than one ToastProvider.
- Do not put essential information only in a toast; it disappears.
- Do not set
politeness="assertive"for confirmations.
Related components
- Alert: Persistent message about a section.
- InlineNotice: Persistent one-line message next to a field, row or card.
- CalloutBanner: Persistent page-level banner.
- NotificationCenter: Planned persistent notification list.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | @radix-ui/react-toastreact |
| Monarch components | None |
Testing coverage
- Shows, runs the action (which dismisses) and dismisses with the close button.
- No axe violations with toasts of every tone open: list items carry no role (regression).
- Danger is announced assertively and other tones politely; politeness can be overridden.
- Never moves focus when a toast appears.
- F8 (or a custom hotkey) moves focus to the named region; Tab reaches the action; Escape dismisses.
- Each Dismiss button is labelled and described by its toast title.
- Keeps at most
limitopen;dismiss()closes all;onDismissfires once each. - Shorthands set the tone; a handle dismisses only its toast.
- Timing: 5s / 10s defaults,
durationScale,Infinity, pause on focus and on hover. - The action uses
altTextfor screen readers. - getErrorMessage: body before transport text, field errors, lists, fallbacks, network message.
- useToast throws outside a provider; renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Cross-browser axe: /components/toast |
Tests: packages/react/src/components/feedback/toast