Toast
A brief, auto-dismissing notification rendered by the Toaster, fired imperatively with toast().
Usage
Mount one Toaster near the root of the app, then call toast() from anywhere: components, stores, or async handlers.
import { Toaster, toast } from '@epilot/spark-ui/toast'// once, at the app root
function App() {
return <Toaster />
}
// anywhere
toast('Settings saved')Use a toast for brief confirmation of an action that just happened ("saved", "removed", "connected"). For messages tied to a spot on the page, use Callout; for anything the user must read or decide on, use Dialog, because toasts auto-dismiss.
Examples
Types
toast.success / info / warning / error pick the matching icon and tinted surface automatically. Rich colors are on by default; pass richColors={false} for neutral surfaces.
With a description
Secondary text below the title, in muted gray on neutral toasts and the tint color on typed toasts. On a typed toast the icon lines up with the title, not with the block as a whole.
With an action
action renders an accent button on the right edge; its onClick runs and the toast dismisses.
Close button
Every toast gets a close button (top-right) by default. Opt out per toast, or with closeButton={false} on the Toaster.
Promise
toast.promise shows a loading spinner, then swaps to the success or error state when the promise settles.
Position
position belongs to the Toaster, not to a single toast, so every toast in an app stacks in the same corner. Mount one Toaster and pick the corner there.
<Toaster position="top-right" />| Value | Stacks from |
|---|---|
"bottom-right" (default) | bottom edge, upward |
"bottom-left", "bottom-center" | bottom edge, upward |
"top-right", "top-left", "top-center" | top edge, downward |
Below sm the corner is ignored. The stack goes full-width and anchors to
one edge, defaulting to the top because that is what the end-customer portal
does, so a block's toasts and the portal's line up. Narrow your browser to see
it. Toasts swipe up, down, or right to dismiss.
Use mobilePosition to anchor it to the bottom instead. Full width stays either
way; only the edge changes.
// bottom-right on desktop, top of the screen on mobile (default)
<Toaster />
// bottom-right on desktop, bottom of the screen on mobile
<Toaster mobilePosition="bottom" />This page has one Toaster at bottom-right, which is why the examples above appear there. There is no live switcher: toast() talks to a single module-level manager, so a second Toaster mounted to demo another corner would render every toast twice.
API Reference
Toaster
Mounts the toast region. Built on Base UI Toast, which owns the stacking, timers, swipe-to-dismiss, and the live region.
| Prop | Type | Default | Description |
|---|---|---|---|
position | "top-left" … "bottom-right" | "bottom-right" | Corner the toasts stack in, from sm up |
mobilePosition | "top" | "bottom" | "top" | Edge the full-width stack anchors to below sm |
closeButton | boolean | true | Close button on every toast; a per-toast value wins |
richColors | boolean | true | Tinted surfaces for success/info/warning/error toasts |
duration | number | 4000 | Auto-dismiss time in ms; 0 disables it |
limit | number | 3 | Toasts shown at once before older ones collapse behind |
className | string | — | Merged onto the viewport |
toast
toast(message, options?) shows a neutral toast and returns its id. The typed variants pick the icon and tinted surface.
| Function | Description |
|---|---|
toast.success / info / warning / error | Typed toast with matching icon and tinted surface |
toast.loading(message) | Spinner toast that stays until dismissed or updated by id |
toast.promise(p, msgs) | Loading, then success or error, driven by the promise |
toast.dismiss(id?) | Dismiss one toast, or all when called without an id |
Common options (second argument):
| Option | Type | Description |
|---|---|---|
description | ReactNode | Secondary text below the title |
action | { label, ...button } | Accent pill on the right edge. Beyond label and onClick it takes any <button> prop, so disabled, className, and type pass through |
closeButton | boolean | Override the Toaster's close-button default |
duration | number | Per-toast auto-dismiss time in ms |
id | string | Update an existing toast instead of adding one |
priority | "low" | "high" | high announces assertively |
onDismiss | () => void | Runs when the toast closes |
Accessibility
- Toasts are announced through a live region, so screen readers pick them up without interrupting the current task. Pass
priority: 'high'for messages that must interrupt. - Timers pause while the pointer is over the region, and F6 moves focus into the notifications landmark.
- The close and action buttons stay out of the accessibility tree until the region is focused or expanded, so a screen reader does not announce controls for every transient toast.
- Don't put the only path to an action in a toast (it auto-dismisses); keep a persistent alternative in the page.