Manual UI Templates Toast

Toast

elements man ui/templates/toast

Short notices that stack at an edge of the screen, pause on hover and close on their own or with a swipe.

Example
import { Toaster, Toast, toast } from "@elements/ui/toast"; import { Button } from "@elements/ui/button"; <!-- once, in the app shell --> <Toaster richColors /> <!-- anywhere in browser code --> <Button onclick={() => toast.success("Event created", { description: "Sunday, December 3 at 9:00 AM", action: { label: "Undo", onClick: () => undo() }, })}>Create event</Button> <Button onclick={() => toast.promise(save(form), { loading: "Saving...", success: (doc) => `Saved ${doc.name}`, error: "Could not save", })}>Save</Button> <!-- declarative: open is two-way --> <Toast open={state.saved} title="Saved" type="success" /> <!-- anchored: next to the button instead of in the stack --> <Button onclick={(e) => toast("Copied", { anchor: e.currentTarget as Element, positioner: { side: "top", sideOffset: 8, arrow: true }, })}>Copy</Button>

Parts

Part What it is
Toaster The region toasts render in: a top-layer <section> with one stack per position. Mount it once, in the app shell.
Toast A declarative toast: shown while open is true; writes open = false back when it closes itself (timer, swipe, close, Escape, action). A literal open is uncontrolled: it shows when it renders.
toast() The imperative API, imported beside Toaster: toast(title, options), toast.success, info, warning, error, loading, message, promise, custom, update, dismiss, getToasts (getActiveToasts) and getHistory. Same id updates a toast in place.
.toast One toast: .toast-close, .toast-icon (loader and icon in one cell), .toast-arrow (anchored, with positioner.arrow), .toast-content (.toast-title, .toast-description), .toast-cancel, .toast-action. A toast.custom() toast holds .toast-custom instead.

Attributes

Toaster

Attribute Type Default Description
position "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" "bottom-right" Where the stack sits. A toast may name its own position; it then renders in a second stack.
expand boolean false Keep the stack fanned out. Otherwise it fans out on hover, focus or the hotkey.
visibleToasts number 3 Toasts past this many are hidden and inert until those in front close.
duration number 4000 Default time on screen in ms. A toast's own duration wins; Infinity stays until dismissed.
gap number -1 px between fanned-out toasts. -1 reads the theme (--space-3-5).
offset string | number | { top, right, bottom, left } "" Distance from the viewport edges. "" reads the theme (--space-6).
mobileOffset string | number | { top, right, bottom, left } "" The same under 600px. "" reads the theme (--space-4).
closeButton boolean false A close button on every toast.
richColors boolean false Success, info, warning and error toasts take their status colors.
invert boolean false Inverse colors: a dark toast on a light page.
theme "" | "light" | "dark" | "system" "" Light or dark toasts whatever the page's mode; system follows the OS. "" follows the page.
hotkey string[] ["altKey", "KeyT"] Focuses the region and fans out the stack. Each entry is a modifier property or an event.code or key, such as ["F8"].
dir "ltr" | "rtl" | "auto" "auto" Text direction of the stacks; auto reads the document.
swipeDirections SwipeDirection[] [] Directions a swipe may dismiss toward. [] derives them from the position (bottom-right: bottom and right).
swipeThreshold number 45 px a swipe must travel to dismiss on release. A quick flick also dismisses.
id string "" Names this Toaster; only toasts with the same toasterId render in it.
label string "Notifications" The region's name; the hotkey is appended.
ariaLabel string "" Replaces the whole region name.
closeButtonLabel string "Close toast" The close button's accessible name.
icons { success?, info?, warning?, error?, loading?, close?: IconName | false } {} Replace or hide the icon per type.
toastOptions { class?, classes?, style?, duration?, closeButton?, unstyled?, closeButtonLabel? } {} Defaults for every toast in this Toaster.
class string "" Extra classes on the region.
style string "" Inline style on the region.

Toast

Attribute Type Default Description
open boolean (two-way) false Shows the toast while true. Bind it; the toast writes false back when it closes itself.
title, description, type string | ((t: ToastItem) => string), string | ((t: ToastItem) => string), ToastType "", "", "default" Its content and kind.
duration, dismissible, closeButton, richColors, invert, important, icon, action, cancel, position, toasterId, swipeDirections, testId, class as the toast() options as the toast() options The same options as toast().
onOpenChange (open: boolean) => void () => {} Called when the toast closes itself; not when the caller sets open.
onEscapeKeyDown, onPause, onResume, onSwipeStart, onSwipeMove, onSwipeEnd, onSwipeCancel functions () => {} As the toast() options.

toast() options

Attribute Type Default Description
description string | ((t: ToastItem) => string) "" Second line under the title. A function is given the toast and re-renders when what it reads changes.
data unknown undefined Your own values, for a function title or description (t.data) and toast.custom().
anchor Element | null null Shows the toast next to this element instead of in the stack.
positioner { side?, align?, sideOffset?, alignOffset?, arrow?, arrowPadding?, collisionAvoidance?, collisionBoundary?, collisionPadding?, sticky?, positionMethod?, disableAnchorTracking?, class?, style? } {} How an anchored toast sits against its anchor; side defaults to top. arrow draws one pointing at the anchor.
swipeDirections SwipeDirection[] the Toaster's The directions that dismiss this toast, over the Toaster's.
id string | number generated Reuse an id to update a toast in place; its timer restarts.
action { label, onClick?, altText?, class?, style? } | Node none A primary button. It closes the toast unless onClick calls e.preventDefault(). altText is read to screen readers. An element of your own renders in its place and keeps its own handlers.
cancel { label, onClick?, class?, style? } | Node none A secondary button; runs onClick and closes. An element of your own renders in its place.
duration number the Toaster's ms on screen; Infinity stays.
dismissible boolean true false: no swipe, no close, no Escape.
icon IconName | false the type's Replace the type icon, or hide it.
important boolean false Announced assertively, interrupting the screen reader.
closeButton, richColors, invert, position, toasterId as on Toaster the Toaster's Override the Toaster's for this toast.
testId, class, classes, style, unstyled string, string, ToastClasses, string, boolean "", "", {}, "", false data-testid, extra classes (per part with classes), inline style, and no chrome.
onDismiss, onAutoClose, onPause, onResume, onEscapeKeyDown, onSwipeStart, onSwipeMove, onSwipeEnd, onSwipeCancel functions none Closed by the user or toast.dismiss; closed by its timer; timer paused and resumed; Escape (preventable); swipe phases.
onClose, onRemove (t: ToastItem) => void none The toast starts to close, for any reason; its exit finished and it left the page.

Keyboard

Keys Action
Alt + T The hotkey: focuses the region and fans out the stack, from anywhere on the page. Pressed again from inside, returns focus.
Tab / Shift + Tab Moves through the toasts, newest first, and their buttons. Tab past the last returns focus to where the hotkey was pressed.
Space / Enter On the action, cancel or close button: runs it and closes the toast.
Escape On a toast: closes it and moves focus to the next. On the region: folds the stack and returns focus.

Data attributes

Attribute Values On Description
data-y-position, data-x-position "top" | "bottom"; "left" | "center" | "right" .toaster-list, .toast
data-expanded "true" | "false" .toaster-list, .toast Fanned out.
data-type "default" | "success" | "info" | "warning" | "error" | "loading" .toast
data-mounted "true" | "false" .toast false for the first frame; the enter transition runs off it.
data-removed "true" | "false" .toast true during the exit.
data-state "open" | "closed" .toast closed during the exit.
data-front "true" | "false" .toast The newest toast.
data-visible "true" | "false" .toast false past visibleToasts.
data-index 0..n .toast
data-swiping, data-swiped, data-swipe-out "true" | "false" .toast
data-swipe "start" | "move" | "cancel" | "end" .toast The phase of a swipe.
data-swipe-direction "left" | "right" | "up" | "down" .toast Where a swiped toast leaves.
data-rich-colors, data-invert, data-dismissible, data-promise, data-styled "true" | "false" .toast
--offset, --index, --z-index, --initial-height, --swipe-amount-x, --swipe-amount-y custom properties .toast Layout and swipe offsets for custom styles.
--toast-index, --toast-height, --toast-offset-y, --toast-swipe-movement-x, --toast-swipe-movement-y custom properties .toast The same numbers under these names too; --toast-frontmost-height on the list.
data-limited, data-starting-style, data-ending-style "true" | "false"; present .toast Past visibleToasts; before the enter; during the exit.
data-update-key, data-updated count; present .toast Updates by id; set again on each, so a CSS animation on it replays.
data-anchored, data-side, data-align, data-anchor-hidden present; side; align .toaster-list An anchored toast's list, as placed against its anchor.
data-behind "true" | "false" .toast-content Behind the front toast.
data-swipe-ignore present your element A press on it never starts a swipe; nor does one on a button, link or field.

Accessibility

  • The region is a <section> named "Notifications alt+T": the hotkey is in its name so it can be found.
  • Each toast is announced when it appears and when its title or type changes (a promise settling): title, description and the action's altText. important toasts use the assertive live region; others the polite one.
  • Each toast is role="status" with aria-live off: named by its title (aria-labelledby) and described by its description (aria-describedby), so a focused toast is read by name; its announcement comes from the live region.
  • Toasts never take focus on their own. The hotkey moves focus into the region; Tab past the last toast, or Escape on the region, returns it.
  • Timers pause while the pointer is over the stack, while keyboard focus is in it, while a toast is pressed (a press released off the stack lets it go), while the window is in the background and while the tab is hidden.
  • Hidden toasts beyond visibleToasts are inert. The close button has an accessible name; icons are decorative. On a non-dismissible toast the close and cancel buttons are aria-disabled.
  • With prefers-reduced-motion, toasts appear, restack and swipe out with no motion.
  • The region is a manual popover in the top layer, re-shown when a toast arrives, so toasts draw above dialogs and popovers opened before them. While a native modal dialog is open the region moves into the dialog's panel (still in the top layer), so its toasts are announced from a live region inside the dialog, and the hotkey, Tab, presses, hover and swipes reach them while the dialog stays open. Escape on a toast closes the toast, not the dialog.

See also