Toast
Short messages about what just happened, stacked in a corner, with a duration that follows the task of the message.
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.
// 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. anywhere:
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
timeoutalways wins.resolvereturns the duration the policy gives, andToast Timeout(options) TOAST_andTIMEOUT_ MS DEFAULT_hold the values.TOAST_ TIMEOUT_ MS Timers pause while the pointer rests on a toast and while the window has no focus.
The manager#
toast.opens a toast and returns its id. Options:add(options) title,description,type(success,error,warning,info,loading, which choose the icon),timeout,action(the props of an action button),Props priority(highannounces assertively), andon.Close toast.andupdate(id, options) toast.change or close it.close(id) toast.shows a loading toast and replaces it with the outcome;promise(promise, {loading, success, error}) successanderrorcan be functions of the result.createmakes a manager of your own (without the duration policy), andToast Manager() usereads the toasts inside a provider, for a list of your own.Toast Manager()
Anatomy#
Toaster renders everything: the provider, the region in the corner and one Toast per message,
with its icon, Toast, Toast, Toast and Toast. The parts are
exported for a toast layout of your own, inside Toast and Toast.
What your application provides#
region(the name of the region) andLabel close(the name of each close button) onLabel Toaster, in your reader's language (required).The text of every toast, and
timeout: 0for a message that asks for an action.
Accessibility and keyboard#
The region is a landmark named by
region; new toasts are announced politely, or assertively withLabel priority: 'high'.F6moves the focus into the region;Tabthen 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#
Import from @workspace/
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
- Type
string
The accessible name of each toast's icon-only close button. Stock's is an English "Close toast".
- Type
string
The accessible name of the toast region, in the reader's language. Base UI's own is an English "Notifications".
- Type
ReactNode
The content of this part, in the forms its type allows.
- Type
number- Default
3
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- attribute) rather than removed, so they can be hidden or animated out.
- Type
number- Default
5000
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.
- Type
ToastManager<any> - Default
toast
A global manager for toasts to use outside of a React component.
Takes only the props listed.
Other exports#
Toast
All props come from the Base UI part this component is built on.
Props
- Type
ToastRoot Toast Object<any>
The toast to render.
- Type
"down""left""right""up"("down" | "left" | "right" | "up"…- Default
['down', 'right']
Direction(s) in which the toast can be swiped to dismiss.
- Type
ComponentRender Fn<…> ReactElement<…>
Allows you to replace the component's HTML element with a different tag, or compose it with another component.
Accepts a React or a function that returns the element to render.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
ToastAction
All props come from the Base UI part this component is built on.
Props
- Type
ComponentRender Fn<…> ReactElement<…> - Default
<Button variant="outline" size="sm" />
Allows you to replace the component's HTML element with a different tag, or compose it with another component.
Accepts a React or a function that returns the element to render.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
ToastClose
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
- Type
string
The accessible name of the icon button, such as "Dismiss".
- Type
ReactNode
A visible label instead of the icon, which then names the button.
- Type
ComponentRender Fn<…> ReactElement<…> - Default
<Button variant="ghost" size="icon-sm" / >
Allows you to replace the component's HTML element with a different tag, or compose it with another component.
Accepts a React or a function that returns the element to render.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
ToastPortal
All props come from the Base UI part this component is built on.
Props
- Type
HTMLElementRefObject<…> ShadowRoot null
A parent element to render the portal element into.
- Type
ComponentRender Fn<…> ReactElement<…>
Allows you to replace the component's HTML element with a different tag, or compose it with another component.
Accepts a React or a function that returns the element to render.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
ToastProvider
All props come from the Base UI part this component is built on.
Props
- Type
ReactNode
The content of this part, in the forms its type allows.
- Type
number- Default
3
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- attribute) rather than removed, so they can be hidden or animated out.
- Type
number- Default
5000
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.
- Type
ToastManager<any>
A global manager for toasts to use outside of a React component.
Takes only the props listed.
ToastViewport
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
- Type
string
The name of the toast region, such as "Notifications" (required).
- Type
ComponentRender Fn<…> ReactElement<…>
Allows you to replace the component's HTML element with a different tag, or compose it with another component.
Accepts a React or a function that returns the element to render.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Parts that take only their element’s attributes
Toast: the attributes of its element, with className and ref, and Base UI'sContent renderto use your own element.Toast: the attributes of its element, with className and ref, and Base UI'sDescription renderto use your own element.Toast: the attributes of its element, with className and ref, and Base UI'sTitle renderto use your own element.
Functions, hooks and constants
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.
Base UI's create: a manager of your own, without the duration policy of toast.
<Data extends object = any>() => ToastManager<Data>- Type
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.
Base UI's use: the toasts and the manager's methods inside a provider.
<Data extends object = any>() => UseToastManagerReturnValue<Data>- Type
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.
- Type
5000
The duration for a toast without a recognised type.
This page adapts the Toast documentation of shadcn/ui (MIT licence) to this package; see Third-party notices.