---
id: "packages/active-ui/toast"
title: "Toast"
description: "Short messages about what just happened, stacked in a corner, with a duration that follows the task of the message."
url: "/packages/active-ui/toast"
locale: "en"
revision: "d510a039acc4df33"
---

# Toast

A toast tells the reader what just happened elsewhere on the screen: saved, failed, sent. It appears
in a corner, stacks with others, and goes away by itself unless it asks for an action. It is built
on Base UI's toast. For a message that must stay in the page, use an [alert](/packages/active-ui/alert).

**Example: Success, error, an action and a promise**

Client: Runs entirely in your browser. Source: `examples/ui/toast.tsx`.

```tsx
// apps/docs/examples/ui/toast.tsx
'use client';

import {Button} from '@workspace/active-ui/components/primitives/button';
import {toast, Toaster} from '@workspace/active-ui/components/primitives/toast';

// Simulated work: settles after a second and a half, without a server.
const sendStatements = () =>
  new Promise<number>((resolve) => window.setTimeout(() => resolve(214), 1500));

/**
 * Short messages about what just happened, in a corner of the screen. Each
 * type keeps the message visible as long as its task needs; a message that
 * asks for an action stays until it is closed. `toast.promise` shows a
 * loading message and replaces it with the outcome (simulated here).
 */
export default function ToastExample() {
  return (
    <Toaster regionLabel="Notifications" closeLabel="Dismiss">
      <div className="flex flex-wrap justify-center gap-2">
        <Button
          variant="outline"
          onClick={() => toast.add({type: 'success', title: 'Scheme saved'})}
        >
          Success
        </Button>
        <Button
          variant="outline"
          onClick={() =>
            toast.add({
              type: 'error',
              title: 'The export failed',
              description:
                'The file was larger than 25 MB. Choose a shorter period.',
            })
          }
        >
          Error
        </Button>
        <Button
          variant="outline"
          onClick={() => {
            const id = toast.add({
              type: 'warning',
              title: 'Three participants need a review',
              description: 'Their date of birth is missing.',
              timeout: 0,
              actionProps: {
                children: 'Mark as reviewed',
                onClick: () => toast.close(id),
              },
            });
          }}
        >
          Action required
        </Button>
        <Button
          variant="outline"
          onClick={() =>
            toast.promise(sendStatements(), {
              loading: {title: 'Sending statements…'},
              success: (count) => ({title: `${count} statements sent`}),
              error: {title: 'The statements were not sent'},
            })
          }
        >
          With progress
        </Button>
      </div>
    </Toaster>
  );
}
```

## Usage

Render one `Toaster` around your application, and call `toast.add` anywhere:

```tsx
import {toast, Toaster} from '@workspace/active-ui/components/primitives/toast';

<Toaster regionLabel="Notifications" closeLabel="Dismiss">
  {app}
</Toaster>;

toast.add({type: 'success', title: 'Scheme saved'});
```

## How long a toast stays

The package's `toast` applies a duration per type, because a message with a reason needs longer
to read than a confirmation:

| Type | Duration |
| --- | --- |
| `success`, `info`, or no type | 5 s |
| `warning` | 8 s |
| `error` | 10 s |
| `loading` | none: it stays until you update or close it |

- **A message that asks for an action stays.** Pass `timeout: 0`: an error is usually a notice the
  reader reads and moves on from; only the place that knows a task is blocked decides that the
  message must stay.
- Your own `timeout` always wins.
- `resolveToastTimeout(options)` returns the duration the policy gives, and `TOAST_TIMEOUT_MS` and
  `DEFAULT_TOAST_TIMEOUT_MS` hold the values.
- Timers pause while the pointer rests on a toast and while the window has no focus.

## The manager

- `toast.add(options)` opens a toast and returns its id. Options: `title`, `description`, `type`
  (`success`, `error`, `warning`, `info`, `loading`, which choose the icon), `timeout`,
  `actionProps` (the props of an action button), `priority` (`high` announces assertively), and
  `onClose`.
- `toast.update(id, options)` and `toast.close(id)` change or close it.
- `toast.promise(promise, {loading, success, error})` shows a loading toast and replaces it with
  the outcome; `success` and `error` can be functions of the result.
- `createToastManager()` makes a manager of your own (without the duration policy), and
  `useToastManager()` reads the toasts inside a provider, for a list of your own.

## Anatomy

`Toaster` renders everything: the provider, the region in the corner and one `Toast` per message,
with its icon, `ToastTitle`, `ToastDescription`, `ToastAction` and `ToastClose`. The parts are
exported for a toast layout of your own, inside `ToastProvider` and `ToastViewport`.

## What your application provides

- `regionLabel` (the name of the region) and `closeLabel` (the name of each close button) on
  `Toaster`, in your reader's language (required).
- The text of every toast, and `timeout: 0` for a message that asks for an action.

## Accessibility and keyboard

- The region is a landmark named by `regionLabel`; new toasts are announced politely, or
  assertively with `priority: 'high'`.
- `F6` moves the focus into the region; `Tab` then moves through the toasts and their buttons.
  Each toast has a close button, which Base UI hides from screen readers until the region has focus,
  so that announcements are not cluttered with it.
- A message that asks for an action does not disappear by itself (`timeout: 0`), so nobody has to
  read it in a hurry.

## API

### Toaster

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `closeLabel` | `string` | yes | The accessible name of each toast's icon-only close button. Stock's is an English "Close toast". |
| `regionLabel` | `string` | yes | The accessible name of the toast region, in the reader's language. Base UI's own is an English "Notifications". |
| `children` | `ReactNode` | no |  |
| `limit` | `number` | no | The maximum number of toasts that can be displayed at once. When the limit is exceeded, the oldest toasts are marked as `limited` (via the `data-limited` attribute) rather than removed, so they can be hidden or animated out. |
| `timeout` | `number` | no | The default amount of time (in ms) before a toast is auto dismissed. A value of `0` will prevent the toast from being dismissed automatically. |
| `toastManager` | `ToastManager<any>` | no | A global manager for toasts to use outside of a React component. |

### Other exports

**Toast**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `toast` | `ToastRootToastObject<any>` | yes | The toast to render. |
| `render` | `ComponentRenderFn<HTMLProps, ToastRootState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `swipeDirection` | `"down" \| "left" \| "right" \| "up" \| ("down" \| "left" \| "right" \| "up")[]` | no | Direction(s) in which the toast can be swiped to dismiss. |

**ToastAction**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ComponentRenderFn<HTMLProps, ToastActionState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastClose**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `aria-label` | `string` | no | The accessible name of the icon button, such as "Dismiss". |
| `children` | `ReactNode` | no | A visible label instead of the icon, which then names the button. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ComponentRenderFn<HTMLProps, ToastCloseState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, ToastContentState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastDescription**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, ToastDescriptionState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastPortal**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `container` | `HTMLElement \| RefObject<HTMLElement \| ShadowRoot \| null> \| ShadowRoot \| null` | no | A parent element to render the portal element into. |
| `render` | `ComponentRenderFn<HTMLProps, ToastPortalState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastProvider**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | no |  |
| `limit` | `number` | no | The maximum number of toasts that can be displayed at once. When the limit is exceeded, the oldest toasts are marked as `limited` (via the `data-limited` attribute) rather than removed, so they can be hidden or animated out. |
| `timeout` | `number` | no | The default amount of time (in ms) before a toast is auto dismissed. A value of `0` will prevent the toast from being dismissed automatically. |
| `toastManager` | `ToastManager<any>` | no | A global manager for toasts to use outside of a React component. |

**ToastTitle**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, ToastTitleState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**ToastViewport**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `aria-label` | `string` | yes | The name of the toast region, such as "Notifications" (required). |
| `render` | `ComponentRenderFn<HTMLProps, ToastViewportState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**resolveToastTimeout**: `(options: { readonly type?: string; readonly timeout?: number; }) => number \| undefined`

The duration this message gets, or `undefined` when it should have none.

Exported because the policy would otherwise be unguarded. A behavioural test
cannot measure it in the test DOM (happy-dom): Base UI pauses every timer
while the window has no focus, and the timer cannot be advanced by a mocked
clock -- a test claiming "gone after five seconds" would therefore be green
or red for the wrong reason. The *mapping* itself can be measured exactly,
and that is precisely what is a decision here.

Fields of `options`:

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | `string` | no | The type of the toast: `success`, `info`, `warning`, `error` or `loading`. |
| `timeout` | `number` | no | A duration chosen by the caller, in ms; it always wins. `0` is persistent. |

**createToastManager**: `<Data extends object = any>() => ToastManager<Data>`

Base UI's `createToastManager`: a manager of your own, without the duration policy of `toast`.

**toast**: `ToastManager<any>`

The same manager, with the duration policy in front. `close`, `update` and
`promise` pass through unchanged: they do not open a new message with its
own duration.

**useToastManager**: `<Data extends object = any>() => UseToastManagerReturnValue<Data>`

Base UI's `useToastManager`: the toasts and the manager's methods inside a provider.

Returns an object with:

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `toasts` | `ToastObject<Data>[]` | yes |  |
| `add` | `<T extends Data = Data>(options: ToastManagerAddOptions<T>) => string` | yes |  |
| `close` | `(toastId?: string) => void` | yes |  |
| `update` | `<T extends Data = Data>(toastId: string, options: ToastManagerUpdateOptions<T> \| ((prevToast: ToastObject<T>) => ToastManagerUpdateOptions<T>)) => void` | yes |  |
| `promise` | `<Value, T extends Data = Data>(promise: Promise<Value>, options: ToastManagerPromiseOptions<Value, T>) => Promise<Value>` | yes |  |

**TOAST_TIMEOUT_MS**: `Record<string, number>`

# How long a toast stays visible

The library has one default duration (5000 ms) and no product policy. That
gave every message the same time: a four-word success confirmation and an
error message containing a reason both disappeared equally fast, although
the second carries more text and still has to be weighed after reading.

Four classes, and they follow the *task* of the message, not its colour:

success / informational the library default; it confirms what the user
just saw happen
warning somewhat longer; it carries a consequence
error without a field twice the default; it contains a *reason*
action required persistent, and explicitly set at the call
site (`timeout: 0`) -- see below

**"Action required" does not follow from `type: 'error'`.** Most errors are
notices: something went wrong, the user reads it and moves on. A message
that may only go away once something has been done is a different kind, and
it should be a deliberate choice at the place that knows the task is
blocked. Making every error persistent would fill the screen with messages
nobody dismisses any more (WCAG 2.2.1 asks the opposite: what asks for an
action must not disappear by itself; what does not, should).

**`loading` never gets a timer**, and that stays so: the library keeps it
until the caller replaces or closes it.

The call site has the last word: its own `timeout` always wins.

**DEFAULT_TOAST_TIMEOUT_MS**: `5000`

The duration for a toast without a recognised `type`.

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Toast documentation of shadcn/ui (MIT licence) to this
> package; see [Third-party notices](/third-party-notices).
