Sonner
Toast notifications for transient feedback — “Label purchased”, “Shipment cancelled”, and the like. Built on sonner, pre-styled with rich colors, Halo status icons, and our theme tokens.
Two pieces: <Toaster />, the host that renders the stack, and toast(), the function you call to push a notification. The app mounts one <Toaster /> at the root (in WMS it lives in __root.tsx; these docs do the same in the layout), then any component calls toast() — no context, no provider wiring at the call site.
Do not mount a Toaster per demo. Sonner’s
toast()is global: every mounted host renders a copy of the same toast. These previews firetoast()against the layout host.
Basic
Call toast() with a message string. It appears bottom-right, auto-dismisses, and stacks with any others.
Variants
toast.success, toast.error, toast.warning, and toast.info add a colored Halo icon and accent. Reach for these to signal outcome — a purchased label is a success, a failed charge is an error. (toast.error is sticky in our wrapper: it stays until dismissed and shows integrated copy and close controls.)
Description and action
The second argument is an options object. description renders a secondary line; action adds a button — { label, onClick } — for a follow-up like copying a tracking number.
Tracked async work
toast.promise tracks client-awaited work whose initiating control is no longer visible. It keeps one Halo mounted while transitioning from neutral pending feedback to success or error. Loading waits 200ms before appearing so fast operations do not flash; errors become sticky and include the standard copy and close controls.
Use this for work such as chunked bulk edits where the result may affect off-screen rows. Do not leave it pending for a background job: transition to “Queued” or “Started” once enqueueing finishes, then let realtime UI own actual progress.
Reference
Toaster
The host component. Mount once near the app root. Extends sonner Toaster props — ours pre-sets richColors, themed style tokens, lucide icons, and a dismiss button, but every prop below can still be overridden.
| Prop | Type | Default | Description |
|---|---|---|---|
theme | "light" | "dark" | "system" | from next-themes | Color scheme; defaults to the active app theme |
position | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "bottom-right" | Where the stack renders |
richColors | boolean | true | Tints success / error / warning / info toasts |
closeButton | boolean | false | Show a close button on every toast |
expand | boolean | false | Expand the stack on hover instead of stacking |
duration | number | 4000 | Default auto-dismiss time in ms for all toasts |
toastOptions | ExternalToast | — | Default options merged into every toast |
toast()
The trigger function, re-exported from sonner with our overrides. Call toast(message, options?), or one of the variant methods. Returns the toast id.
| Method | Signature | Description |
|---|---|---|
toast | (message, options?) => id | Default neutral toast |
toast.success | (message, options?) => id | Success variant (green, check icon) |
toast.error | (message, options?) => id | Error variant — sticky with copy and close controls |
toast.warning | (message, options?) => id | Warning variant (amber, circular alert icon) |
toast.info | (message, options?) => id | Info variant (blue, info icon) |
toast.promise | (promise, { loading, success, error, delayMs? }) => result | Tracks awaited work with one transitioning Halo; result.unwrap() returns the original promise |
toast.message | (message, options?) => id | Explicit neutral toast (alias of the default call) |
toast.dismiss | (id?) => void | Dismiss a specific toast, or all when called with no id |
Options (second argument)
The options object (ExternalToast) shared by all toast methods. Common fields:
| Option | Type | Description |
|---|---|---|
description | ReactNode | Secondary line under the message |
action | { label, onClick } | A button rendered in the toast for a follow-up action |
duration | number | Auto-dismiss time in ms (Infinity to keep it open) |
closeButton | boolean | Show a close button on this toast |
id | string | number | Reuse an id to update an existing toast in place |
onDismiss | (toast) => void | Fires when the toast is dismissed |
onAutoClose | (toast) => void | Fires when the toast auto-closes after its duration |