Toast
NewA short message at the edge of the window about something that just happened: an order fulfilled, an export started, a payment that failed. It leaves on its own, so it never carries anything the merchant has to act on.
Basics
Mount one Toaster near the root of the app, then call toast from anywhere: an event handler, a mutation's onSuccess, a hook. The Toaster draws nothing until a toast arrives. Each toast rises in from the bottom right on the Orbit spring, stays a few seconds and slips back out.
Tones
Five tones and a plain message. The surface never changes: only the icon carries the tone, in its status colour. A loading toast shows the orbit spinner and stays until you update it with the same id, as the Loading button does after a moment.
| Tone | Call | Stays for | Use it for |
|---|---|---|---|
| Success | toast.success | 4 seconds | Something the merchant did worked. |
| Error | toast.error | 8 seconds | Something did not work. Say what failed and what to do next. |
| Warning | toast.warning | 6 seconds | It worked, with a catch worth knowing about. |
| Info | toast.info | 5 seconds | Something happened that the merchant did not do here: an export started, a new order came in. |
| Loading | toast.loading | Until it ends | Work that finishes on its own. Turn it into its outcome, or use toast.promise. |
| Message | toast | 4 seconds | A quiet confirmation with no tone: a link copied, a draft saved. |
In both themes
Held still for review, light beside dark. The surface matches the popover's (raised, 14px corners, the floating shadow) and adds a hairline ring, so its edge holds over any page. The title is in the UI weight and the description is muted. In dark the surface lifts to graphite and the status colours brighten to stay readable.
Long content
Titles and descriptions wrap; the icon and close button stay with the first line.
Inside a ThemeScope
A Toaster inside a ThemeScope takes the scope's theme, as the kit's other overlays do; anywhere else it follows the page. Each side here has a Toaster of its own, framed to its panel.
With and without a description
The title says what happened. Add a description only when the merchant needs one more fact: what happens next, or what changed.
Actions
One follow-up, as a small button under the text: Undo, Try again, View order. The toast closes once the action has run. Call event.preventDefault() in onClick to keep it open; an action on a loading toast never closes it.
Undo
altText is the button's accessible name, for when the label alone would be unclear out of context. Keep the label's words in it: if they are missing, the label is put in front.
Waiting for the action
Return a promise from onClick and the button keeps the comet in orbit until it settles. The toast closes when it resolves and stays when it rejects. This one tries again for a moment, then reports back.
Loading and promises
toast.promise shows a loading toast while the work runs, then turns it into the outcome in place: the spinner gives way to the tone icon, which lands with a small settle. Each state is a title, or a { title, description }; success and error can also be functions of the result or the error. Leave one out and the toast simply closes. It returns the toast's id straight away.
Progress, updated in place
Call toast.loading again with the same id to update it, and finish with toast.success or toast.error on that id. The toast keeps its place in the stack throughout. Dismiss it yourself if the work is abandoned.
How long they stay
Each tone has its own time on screen, longest for errors because they say what went wrong and what to do about it. A loading toast stays until it is updated or dismissed. Pass duration in milliseconds to change one, or Infinity to keep it until it is closed.
| Tone | Default duration |
|---|---|
toast | 4,000ms |
toast.success | 4,000ms |
toast.info | 5,000ms |
toast.warning | 6,000ms |
toast.error | 8,000ms |
toast.loading | Infinity |
- Every timer pauses while the pointer is over the toasts, while the stack is fanned out, and while the browser tab is in the background.
- An update by id restarts the timer, so a loading toast that turns into its outcome gets the outcome tone's full time.
Stacking and position
The newest toast sits in front. Up to two more fold behind it, each 12px further back and 5% smaller, with their content hidden. Hover the stack (or press Alt+T) and it fans out, 12px apart, with their timers paused. Swipe a toast towards the edge it sits against to dismiss it.
Position
Toasts appear at the Toaster's position, the bottom right by default, 24px in from the edges. A toast can choose its own with position, for a screen where that corner is busy; try it in the playground. On screens up to 600px wide they span the width, 16px in from each side.
Playground
Build a toast, show it, and copy the call. Toasts follow the site theme: switch it in the header.
toast.success('Order #1042 marked as fulfilled', {
description: 'We emailed James Okafor his tracking link.',
action: { label: 'Undo', onClick: undo },
})Guidelines
- Use a toast to confirm something the merchant just did, or to report work that finished in the background.
- A toast leaves on its own, so it is never the only place something important is said. Errors the merchant has to fix belong next to the problem: an error on the Field, or an Alert at the top of the form.
- Never ask a question in a toast. A decision wants a dialog.
- Title it with what happened, in a few words and sentence case, with no full stop. Screen readers read the title without the icon, so it has to carry the tone itself.
- One action at most, and never the only way to do something: an Undo should also be possible from the page or its history.
- Do not toast every autosave or every row of a bulk change. When several things finish together, say so once: "Updated 12 products".
- Never raise a toast as a page loads. Toasts answer something that just happened.
Accessibility
- The Toaster is a polite live region: screen readers read each toast as it arrives, without moving focus.
- Alt+T (Option+T on a Mac) moves focus to the toasts and fans the stack out. Tab reaches each toast, its action and its close button; Escape folds the stack again.
- The close button is named "Dismiss" (
closeLabelon the Toaster changes it, for example in another language). An action'saltTextbecomes its accessible name. - Timers pause while the pointer is over the toasts and while the stack is fanned out, so there is time to read and reach them.
- On touch screens a swipe towards the edge dismisses a toast. Loading toasts end with their work, so they cannot be dismissed unless they pass `dismissable: true` (for work that runs for minutes).
- While a modal dialog is open, the page behind it takes no clicks, toasts included. They still appear and are read out.
- With reduced motion, toasts fade in and out where they stand, and the stack rearranges without sliding.
Props
| Call | Returns | What it does |
|---|---|---|
toast(title, options?) | ToastId | A plain message, with no tone icon. |
toast.success / error / warning / info (title, options?) | ToastId | A toast in that tone. |
toast.loading(title, options?) | ToastId | Stays until updated with its id, or dismissed. |
toast.promise(work, options) | ToastId | Loading while the work runs, then its outcome. |
toast.dismiss(id?) | void | Closes one toast, or every toast without an id. |
Toast options
| Prop | Type | Default | Description |
|---|---|---|---|
description | ReactNode | None | One more fact, under the title. |
action | ToastAction | None | One follow-up, as a small button under the text. See below. |
duration | number | by tone | Time on screen in milliseconds. Infinity keeps it until it is closed. |
id | string | number | None | Reuse the id of a toast that is showing to update it in place. |
dismissable | boolean | true | false hides the close button and turns off swipe to dismiss. Also accepted as dismissible. |
position | ToastPosition | None | This toast's corner, instead of the Toaster's. |
icon | ReactNode | None | Replaces the tone's icon. null shows none. |
onDismiss | (id) => void | None | Runs when it is closed: its close button, a swipe or toast.dismiss. |
onAutoClose | (id) => void | None | Runs when its time runs out. |
toasterId | string | None | Sends it to the Toaster with this id, on a page with more than one. |
ToastAction
| Prop | Type | Default | Description |
|---|---|---|---|
labelRequired | ReactNode | None | The button's words: a verb, like "Undo". |
onClickRequired | (event) => void | Promise<unknown> | None | Runs on click, then the toast closes. Call event.preventDefault() to keep it. Return a promise to show the busy state until it settles. |
altText | string | None | The accessible name, when the label needs context. Keep the label's words in it. |
variant | 'default' | 'destructive' | 'default' | destructive turns the button red. |
toast.promise options
| Prop | Type | Default | Description |
|---|---|---|---|
loadingRequired | ReactNode | { title, description? } | None | Shown while the work runs. |
success | ReactNode | { title, description? } | (result) => … | None | Shown when it resolves. Leave it out and the toast closes. |
error | ReactNode | { title, description? } | (error) => … | None | Shown when it fails. Leave it out and the toast closes. |
… | Toast options | None | Every toast option except description and icon. duration applies to the outcome. |
Toaster
| Prop | Type | Default | Description |
|---|---|---|---|
position | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'bottom-right' | Where toasts appear. |
gap | number | 12 | Space between toasts when the stack fans out, and how far each folded toast peeks out. |
offset | number | string | { top, right, bottom, left } | 24px | Distance from the window's edges. |
mobileOffset | number | string | { top, right, bottom, left } | 16px | The same on screens up to 600px wide, where toasts span the width. |
visibleToasts | number | 3 | How many show in the stack at once. |
expand | boolean | false | Keep the stack fanned out instead of folded. |
closeLabel | string | 'Dismiss' | Accessible name of each toast's close button. |
hotkey | string[] | ['altKey', 'KeyT'] | Keys that focus the toasts and fan them out. An empty array turns it off. |
containerAriaLabel | string | 'Notifications' | Name of the region that holds the toasts. |
container | HTMLElement | null | document.body | Where the toasts are portalled. |
id | string | None | Names this Toaster, for toasts sent with toasterId. |