Toast and Sonner
Use toast and sonner with the current published Askr packages.
@askrjs/ui/toast0.0.13@askrjs/themes/toast0.0.13@askrjs/themes/sonner0.0.13
How to use toast and sonner
Toast and Sonner should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.
- Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
- Keep toast and sonner labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
- Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { Toast, ToastDescription, ToastHost, ToastTitle } from '@askrjs/themes/components';
<ToastHost>
<Toast open={saved()} onOpenChange={saved.set}>
<ToastTitle>Project saved</ToastTitle>
<ToastDescription>Your changes are now visible.</ToastDescription>
</Toast>
</ToastHost>Purpose
Toast is the headless notification primitive: a host component that owns a registry of active entries, plus the individual pieces you compose into each one. Sonner is a themes-only alternate — a single component you mount once, with no `@askrjs/ui` equivalent — for teams who want a drop-in notifier instead of assembling ToastHost, ToastViewport, and Toast by hand.
Install and import
Headless pieces — ToastHost, ToastViewport, Toast, ToastTitle, ToastDescription, ToastAction, ToastClose — come from `@askrjs/ui/toast`. The themed layer re-exports that same set from `@askrjs/themes/toast`, and separately exposes `@askrjs/themes/sonner`, which is its own API with no headless counterpart to import instead.
Live examples
Trigger both systems from this page to see how their stacking, timing, and dismiss behavior actually differ in the browser, not just on paper. Because they're separate implementations, watch the DOM structure each one produces — they don't share markup even when the visual result looks similar.
Anatomy
ToastHost sits near the root of your tree and owns the registry plus a default `duration`; ToastViewport renders the live region the stack mounts into. Each notification is declared with a Toast, which returns no DOM of its own, composed from ToastTitle, ToastDescription, and optionally ToastAction or ToastClose for interactive controls. Sonner skips all of that composition — it's a single mounted piece, closer in spirit to the themed catalog's Toaster than to the ToastHost/Toast model.
State model
An individual Toast takes `open`/`defaultOpen`/`onOpenChange` like the other disclosure primitives, plus its own `duration` (falling back to ToastHost's default when omitted) and a `variant` of `'default' | 'success' | 'warning' | 'danger' | 'info'`. Setting `duration` on ToastHost changes that fallback for every toast underneath it that doesn't specify one itself.
Keyboard and accessibility
The stack renders with `role="status"` and `aria-live="polite"`, per TOAST_A11Y_CONTRACT, so new toasts get announced without stealing focus, and the viewport carries an accessible "Notifications" label out of the box. ToastAction and ToastClose are ordinary focusable buttons; ToastAction runs its handler and then closes the toast on its own, so there's no need to also wire a separate dismiss call after the action fires.
Styling and tokens
`data-slot`, `data-state`, and `data-disabled` drive the themed enter/exit transitions for the ToastHost/Toast family. Sonner is a distinct API with its own presentation, so don't assume its class names or data attributes line up with Toast's — write separate styles for each rather than sharing selectors across them.
API
ToastHostProps adds `duration` on top of standard div props; ToastProps adds `variant`, `open`/`defaultOpen`/`onOpenChange`, and `duration`. ToastAction and ToastClose use the same button-like prop shape as other interactive primitives in the library, including `asChild`.
Edge cases
Toast and Sonner solve the same problem with two different composition models, so mixing them in one application means running two separate notification stacks side by side — pick one per app rather than splitting notification types between them. Because ToastHost owns the registry, a Toast rendered without a ToastHost above it has nowhere to register into, so mount ToastHost once near your app root instead of per page.
Related pages
See Progress for status feedback with a determinate value instead of a transient message, and Alert, Badge, Empty, Skeleton, and Spinner for feedback that should stay on screen rather than time out.