Source: https://monarchui.dev/components/toast/

Components · Status and feedback

# Toast

Transient notifications from useToast(), rendered by ToastProvider.

@rookcom/monarch-react

Component

## Success with undo

## Usage examples

### Errors from an API

### More time for people who need it

## 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()` from `useToast()` 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

```tsx
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’s `durationScale`. `Infinity` on either keeps it until dismissed.
- Pauses on hover and focus, and resumes when both leave.
- Activating the action runs `onClick` and 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

label

plus the hotkey ("Notifications (F8)"); each toast by its title.

- Each toast is announced once through a separate live region: `aria-live="assertive"` for danger (or `politeness="assertive"`), `polite` otherwise. 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, `durationScale` gives 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 `getErrorMessage` so people never see raw server or transport text.
- Wire `durationScale` to 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](https://monarchui.dev/components/alert/): Persistent message about a section.
- [InlineNotice](https://monarchui.dev/components/inline-notice/): Persistent one-line message next to a field, row or card.
- [CalloutBanner](https://monarchui.dev/components/callout-banner/): Persistent page-level banner.
- [NotificationCenter](https://monarchui.dev/components/notification-center/): Planned persistent notification list.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `@radix-ui/react-toast``react` |
| 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 `limit` open; `dismiss()` closes all; `onDismiss` fires 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 `altText` for 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`

## All example code

### Success with undo

```tsx
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>
```

### Errors from an API

```tsx
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

```tsx
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>
```
