A short message that floats in at the bottom of the screen to confirm what just happened, such as "Link copied", in Alert's look and intents, and leaves again on its own after a few seconds, with an optional title, an action and a close button.
When to use
Use Toast to confirm in a few words what just happened, such as "Link copied", in a message that leaves on its own after a few seconds.
| Use instead | When |
|---|---|
| Alert | The message must stay while its cause lasts, such as a signal fault, set into a panel |
| AlertDialog | A failure must stop the task until the person answers |
Kongens Nytorv
Metro M1, M2 and M3.
Variants and states
| Rest | |
|---|---|
| Neutral | |
| Success | |
| Warning | |
| Danger | |
| Info |
- Rest
- Rest
- Rest
- Rest
- Rest
Props
| Name | Type | Default | Description |
|---|---|---|---|
color | 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | The intent: what the message means. neutral for a plain notice, success for something that went well, warning for something to take care with, danger for something that failed, info for something worth knowing; always in words that say it too. Set on the root as data-color |
title | ReactNode | — | The message in a few words, which names the toast: "Link copied". Replaces the HTML title attribute |
children | ReactNode | — | What happened, in a plain sentence, below the title: "Copy it from the address bar instead." Without a title, it names the toast |
icon | ReactNode | — | Replaces the intent's icon, for the eye only: it is hidden from screen readers. null or false shows no icon; left out, the intent's own shows, and neutral has none. It renders at 13 px, icon.size.325, so give an icon drawn on the 14 px grid, or set --perron-icon-size to its step's icon.size.* through slotProps.icon |
action | ReactNode | — | What the person can do about it, such as a small outline Button "Undo", below the sentences. With an action, the toast stays until it is closed, unless autoHideDuration is given |
open | boolean | — | Whether the toast is shown. It shows each time open turns true and leaves when it turns false; once it closes itself, set open to false in onClose, so the next true shows it again |
defaultOpen | boolean | false | Whether the toast shows when it mounts, when open is left out. It closes itself and does not show again |
onClose | (event: Event, reason: ToastCloseReason, details: ToastCloseDetails) => void | — | Called when the toast closes itself: reason is 'close-press' for its close button, 'escape-key' for Esc, 'swipe' for a swipe or 'timeout' once autoHideDuration has passed. Not called when open turns false |
autoHideDuration | number | null | 5000, or null with an action | How long the toast shows, in milliseconds, before it hides itself; null keeps it until it is closed. The time pauses while the pointer or the keyboard is in the region, or the window is in the background |
closeLabel | string | The locale's | Overrides "Close", the close button's name |
ref | Ref<HTMLDivElement> | — | Reaches the root, the toast |
slotProps | { icon?: ComponentProps<"div", {}, HTMLProps> | undefined; content?: ToastContentProps | undefined; title?: ToastTitleProps | undefined; description?: ToastDescriptionProps | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; close?: Omit<ToastCloseProps, "children" | "disabled"> | undefined; } | — | Props for each part: icon, content, title, description, action, close. |
color'neutral' | 'success' | 'warning' | 'danger' | 'info', default'neutral'- The intent: what the message means.
neutralfor a plain notice,successfor something that went well,warningfor something to take care with,dangerfor something that failed,infofor something worth knowing; always in words that say it too. Set on the root asdata-color titleReactNode, default —- The message in a few words, which names the toast: "Link copied". Replaces the HTML
titleattribute childrenReactNode, default —- What happened, in a plain sentence, below the title: "Copy it from the address bar instead." Without a
title, it names the toast iconReactNode, default —- Replaces the intent's icon, for the eye only: it is hidden from screen readers.
nullorfalseshows no icon; left out, the intent's own shows, andneutralhas none. It renders at 13 px,icon.size.325, so give an icon drawn on the 14 px grid, or set--perron-icon-sizeto its step'sicon.size.*throughslotProps.icon actionReactNode, default —- What the person can do about it, such as a small outline Button "Undo", below the sentences. With an action, the toast stays until it is closed, unless
autoHideDurationis given openboolean, default —- Whether the toast is shown. It shows each time
openturnstrueand leaves when it turnsfalse; once it closes itself, setopentofalseinonClose, so the nexttrueshows it again defaultOpenboolean, defaultfalse- Whether the toast shows when it mounts, when
openis left out. It closes itself and does not show again onClose(event: Event, reason: ToastCloseReason, details: ToastCloseDetails) => void, default —- Called when the toast closes itself:
reasonis'close-press'for its close button,'escape-key'for Esc,'swipe'for a swipe or'timeout'onceautoHideDurationhas passed. Not called whenopenturnsfalse autoHideDurationnumber | null, default 5000, or null with an action- How long the toast shows, in milliseconds, before it hides itself;
nullkeeps it until it is closed. The time pauses while the pointer or the keyboard is in the region, or the window is in the background closeLabelstring, default The locale's- Overrides "Close", the close button's name
refRef<HTMLDivElement>, default —- Reaches the root, the toast
slotProps{ icon?: ComponentProps<"div", {}, HTMLProps> | undefined; content?: ToastContentProps | undefined; title?: ToastTitleProps | undefined; description?: ToastDescriptionProps | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; close?: Omit<ToastCloseProps, "children" | "disabled"> | undefined; }, default —- Props for each part:
icon,content,title,description,action,close.
Use it
import { Toast } from '@mylder/perron'Ask your agent
get_reference({ library: "base-ui", component: "Toast" })Keyboard and ARIA
From the Base UI reference and its source, node_modules/@base-ui/react/toast/. Every key below works while at least one toast is open; with none, the region takes no keys.
| Key | Does | From |
|---|---|---|
F6 | From anywhere on the page, moves focus to the region and pauses every toast's timer; focus remembers where it was | Base UI |
Tab | From the region, moves to the newest toast, then into it, to its action's controls and its close button, then to the next toast; past the last, back to where focus was before F6 | Base UI, and the browser inside a toast |
Shift+Tab | Moves back through them; from the region itself, back to where focus was before F6 | Base UI, and the browser inside a toast |
Escape | With focus in a toast, closes it: onClose is called with 'escape-key', and focus moves to the next toast, or the one before it when it was the last, or back to where it was before F6 | Base UI; Perron calls onClose |
Enter, Space | On the close button, closes the toast: onClose is called with 'close-press', and focus moves as for Escape | The browser and Base UI; Perron calls onClose |
Focus leaving the region and the pointer leaving it start the timers again.
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
viewport | region, a polite live region that reads each toast as it arrives | The owned "Notifications", through aria-label (CR-3) | None |
root | dialog, non-modal (aria-modal="false"), in the tab order | Its title through aria-labelledby; without one, its description (CR-9) | None |
icon | Hidden: aria-hidden="true" | None | None |
title | Paragraph, or a heading when slotProps.title.render makes it one | Its text, from title | None |
description | None (generic div); the root's description through aria-describedby | None | None |
action | None (generic div); the caller's controls carry their own roles | The caller's controls' own names | Theirs |
close | button, native, never aria-hidden (CR-7) | The owned "Close", or closeLabel, in the CloseButton's VisuallyHidden span | None |
A toast is read as it arrives because the region is a polite live region already on the page (CR-2): its title, its sentences, its action's names and "Close", in that order. It interrupts nothing. Toast adds no hidden word for its intent, as Alert adds none: the caller's words say what the colour and the icon show.
With no title, the toast is named by its description, the root's aria-labelledby pointing at the description and its aria-describedby left off: a dialog with no name fails axe's aria-dialog-name rule, which the stories run in error mode, a toast such as "Link copied." often has a sentence and no title, and naming it by the sentence and not describing it by the same sentence reads it once.
The close button is never aria-hidden, where Base UI's Toast.Close hides it from screen readers while the region is neither hovered nor holding focus: Toast sets aria-hidden={false} after Base UI's, since a focusable button inside aria-hidden fails axe's aria-hidden-focus rule, which the stories run in error mode, and a person reading the region with a screen reader, without F6, must find the button that closes a toast that stays; the cost is "Close" read after each toast's words as it arrives.
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the colour, the tint and the icon repeat what the caller's words say, and the icon is
aria-hidden; thecolorJSDoc asks for words that say the intent, as Alert's does. A toast that hides itself is read by the region as it arrives, so the ear hears it even where the eye looks elsewhere; one that leaves before it is read pauses while focus or the pointer is in the region. - Every landmark and named region has an accessible name: the region is named "Notifications" from the locale, or
PerronProvider'snotificationsLabel(CR-3); each toast, adialog, by its title or its description (CR-9). - Every default label can be overridden:
closeLabelon each Toast,notificationsLabelonPerronProviderfor the shared region. - Every shape the type accepts renders, in every variant:
title,children,iconandactioneach render a string, an element and an array, as Alert's do;iconasnullorfalserenders no icon part. - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: a toast past Base UI'slimitisinertand not displayed; a leaving toast is gone from the list once its transition ends, and Toast keeps no copy of its own. - Native semantics first: Base UI's
regionanddialogroles, apfor the title, a native<button type="button">to close, the caller's ownbuttonora hrefin the action; Toast addsaria-labelledbywhen there is no title (CR-9) andaria-hidden="false"on the close button (CR-7), and no other ARIA. - Focus moves in reading order and returns on close: a toast never takes focus when it arrives; F6 reaches the region, Tab walks the toasts from the top, as they stand, newest first, and inside each its action, then its close button, as on screen; closing a focused toast moves focus to the next toast, or to the one before it when it was the last, or back to where it was before F6 (Base UI).
- Every target is at least
size.target-minsquare: the close button issize.control, 34 px; the action's targets are the caller's components, a small Buttonsize.button-sm. - Loops stop when the system asks for less motion: none here; the slide in and out is a transition on
duration.toast, which the token's reduced-motion value takes to 0.01 ms.
Owned strings
Toast owns the close button's name, the key en.ts has, and the region's name, a new key. Every other word comes from the caller, through title, children and action, and the icon is hidden from screen readers.
| Key | English | Override | Seen or heard where |
|---|---|---|---|
close | "Close" | closeLabel | close, heard as the button's name through the VisuallyHidden part; the key en.ts has, whose JSDoc reads "A button that closes a dialog, panel or notice." |
notifications | "Notifications" | notificationsLabel on PerronProvider | viewport, heard as the region's name when a screen reader lists landmarks or enters it with F6; new, with the JSDoc "The region at the bottom of the screen that holds the toasts." |
The region's name is the new owned key notifications, "Notifications", overridden by a notificationsLabel prop on PerronProvider, which renders the one region, and not on Toast: Base UI's viewport names itself "Notifications" in English in every locale, so Perron replaces it with the locale's word; the region is shared, so a prop on one toast would rename it for every other; and the contract asks for a *Label override for every owned string, which PerronProvider, the region's owner, takes. A Toast with no PerronProvider names its own region with the English word, as every owned string reads without a provider.
Do and don't
For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Confirm what just happened, in a few words: "Link copied", "Filters cleared" | Write "Success!" or "Something went wrong": content design has no exclamation marks, and the words should say what happened | review |
| DD-2 | must | Keep the app inside one PerronProvider, so every toast stacks in one region at the bottom of the screen and is read by one live region | Render Toasts with no provider: each gets a region of its own, and two at once overlap | review |
| DD-3 | must | Set open to false in onClose, so the next true shows the toast again | Leave open true after the toast has closed itself: it doesn't show again until open turns false and true | review |
| DD-4 | must | Give a toast an action only for what can't wait, such as "Undo", and let it stay until it is closed | Give an action toast a short autoHideDuration: a person still hearing the toast can miss the action, and it may be the only way to undo | review |
| DD-5 | must | Put an app-wide context an action needs, such as a router's, around PerronProvider: the toast renders in PerronProvider's region, not where it is written | Rely on a context between PerronProvider and the Toast in the action: the region renders outside it | review |
| DD-6 | must | Show a message that must stay while its cause lasts, such as a signal fault, as an Alert in the panel | Use a toast for something the person needs later: it leaves after five seconds | review |
| DD-7 | must | Use an AlertDialog for a failure that must stop the task | Use a toast to interrupt: every toast is polite, and waits for the screen reader to finish | review |