Toast (Sonner)
Transient, non-blocking feedback after an action. Built on Sonner (shadcn's official toast library since 2024, replacing the deprecated Radix Toast). Chrome per Figma spec: bg-popover rounded-md border shadow-md p-4 gap-2, title font-medium 14, description muted 14, optional Phosphor icon size-5, optional primary/secondary action button. Yuno convention: success messages include 'successfully' + period.
Anatomy
Two shapes share the same chrome. The default Sonner is a single-row toast (icon + text stack + optional action). Sonner / Notification is a richer variant that top-aligns, stacks a Progress bar between text and action, and adds an explicit close X — reserved for long-running or dismissible operations.
Exporting - Payments.csv
Loading...
- 1Toaster (Root)
The <Toaster /> mounted once at the app root. Owns position, theme, icons, and the classNames overrides that apply to every toast. Yuno rule for positioning: default toasts render bottom-center of the viewport (48px from bottom); Sonner / Notifications override to top-right — 88px from top (64px topbar + 24px gap) and 24px from the right edge — via toast(..., { position: 'top-right' }).
- 2Icon slot
Optional Phosphor size-5 icon at the left. Yuno wires five weights=light: CheckCircle (success) · Info (info) · Warning (warning) · WarningCircle (error) · CircleNotch (loading, animated with animate-spin).
- 3Title
font-medium text-sm text-popover-foreground. First line of the toast. Keep it short — one clear statement of what happened.
- 4Description
Optional. font-normal text-sm text-muted-foreground. Sits below the title with gap-[2px]. Use when the title alone doesn't carry enough context (a file name, a next-step hint).
- 5Action button
Optional primary button on the right (h-6 px-2 rounded-sm text-xs font-medium bg-primary). Canonical labels: 'Undo' (reversible mutation), 'Retry' / 'Try again' (recover from an error), 'Download again' (re-run an idempotent op).
- 6Cancel button
Optional secondary button (bg-secondary text-secondary-foreground). Canonical label: 'Cancel'. Used alone or as a companion to the action.
- 7Progress bar (Notification only)
h-2 rounded-full track with a primary-tinted fill. Sits between the text stack and the action row in a Sonner / Notification. Wire it to the actual operation progress — never fake it.
- 8Close X (Notification only)
Explicit dismiss control (16px X icon) top-right of a Sonner / Notification. Enabled globally via <Toaster closeButton /> or per-toast via the close button visibility.
States
Default toasts animate in from the bottom-center (slide up + fade + scale). Notifications animate in from the top-right corner. Idle: bg-popover with shadow-md drop-shadow. Focus (keyboard swipe target): outer ring-2 ring-black/20 + shadow-md — Sonner handles this on its own; do not override. Loading: the icon rotates via animate-spin. Hover/near a toast: Sonner expands the stack if `expand` is enabled — the kit leaves it collapsed so the surface stays quiet.
Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
Every success message follows the Yuno copy convention: verb + noun + 'successfully' + period. Uses CheckCircle (light) in text-success as the icon. Keep the message under 60 characters so it never wraps.
toast.success("Rule created successfully.");For reversible mutations (delete a rule, remove a member, unarchive an item), pair the success message with an Undo action that reverts the change. Sonner's action prop takes { label, onClick } and renders the primary h-6 button. Undo timers should mirror the toast duration.
toast.success("Rule deleted successfully.", {
action: { label: "Undo", onClick: () => restoreRule() },
});For recoverable failures (a network hiccup, a 500 the user can retry). Uses WarningCircle (light) in text-destructive. Copy: 'Something went wrong. Try again.' + action { label: 'Try again', onClick: retry }.
toast.error("Something went wrong. Try again.", {
action: { label: "Try again", onClick: () => retry() },
});For blocked-but-non-critical actions (out-of-range dates, unsaved changes). Uses Warning (light) in text-amber-500. Keep it stateless — describe why the action didn't complete, don't ask a question.
toast.warning("Event start time cannot be earlier than 8am.");For contextual FYIs (a config just changed, a new report is ready). Uses Info (light) in text-primary. Use sparingly — inline hints usually beat a toast for FYIs.
toast.info("Be at the area 10 minutes before the event time.");For operations you already have a Promise for (async save, remote export). toast.promise(fn, { loading, success, error }) fires the loading toast immediately, then swaps to success or error when the promise resolves. Uses CircleNotch (light) with animate-spin as the loading icon.
toast.promise(saveRule(), {
loading: "Saving rule…",
success: "Rule saved successfully.",
error: "Could not save the rule. Try again.",
});Uses the Sonner / Notification variant for operations the user watches finish. Fire it with position:'top-right' so it renders 88px from the top (below the topbar) and 24px from the right edge — the Yuno canon for notifications. duration:Infinity + a Progress bar rendered inside description + Cancel action + close X. Canonical Yuno case: 'Exporting - File title.csv' with a live progress track. Never fake the progress — wire it to the real percentage.
toast("Exporting - File title.csv", {
description: (
<div className="flex flex-col gap-2">
<span>Loading…</span>
<Progress value={progress} />
</div>
),
duration: Infinity,
position: "top-right",
closeButton: true,
action: { label: "Cancel", onClick: () => cancelExport() },
});Rich confirmation with CheckCircle (fill) in text-success. Same top-right positioning + duration:Infinity so the user decides. Canonical Yuno case: after a completed export, ask 'Download file again?' with a primary 'Download again' action.
toast.success("Download file again?", {
description: "Do you want to download 'Pagos Argentina.csv' again?",
duration: Infinity,
position: "top-right",
closeButton: true,
action: { label: "Download again", onClick: () => downloadAgain() },
});Rich confirmation with WarningCircle (fill) in text-destructive. Canonical Yuno case: 'Failed export! - File title.csv' with a primary 'Try again' action. Description explains what went wrong; action offers the recovery step.
toast.error("Failed export! - File title.csv", {
description: "We're sorry, something went wrong and we couldn't download your report.",
duration: Infinity,
position: "top-right",
closeButton: true,
action: { label: "Try again", onClick: () => retryExport() },
});Rich confirmation with Warning (fill) in text-amber-500 for non-critical warnings the user should acknowledge. Canonical Yuno case: 'Failed export! - File title.csv' explaining a slight delay, with a single 'Got it!' primary action to dismiss.
toast.warning("Failed export! - File title.csv", {
description: "The report's larger size is causing a slight delay. We're processing the download and will notify you when it's done.",
duration: Infinity,
position: "top-right",
closeButton: true,
action: { label: "Got it!", onClick: (id) => toast.dismiss(id) },
});Import
Copy the Toaster mount once in the app layout, then call toast() from anywhere. The Yuno wrapper re-exports the sonner toast() function so you can import both from the same path.
// app/layout.tsx — mount once
import { Toaster } from "@/components/ui/sonner";
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Toaster />
</body>
</html>
);
}
// anywhere — fire a toast
import { toast } from "sonner";
toast.success("Rule created successfully.");Props
The Yuno wrapper opinionates position, icons, and classNames; you rarely need to pass anything to <Toaster />. The props you actually work with are the toast() function options — Sonner's public API for firing individual toasts.
| Prop | Type | Default | Description |
|---|---|---|---|
| toast(message, options) | (message: ReactNode, options?: ToastOptions) => id | — | Default toast. Renders the message as the title. Returns the toast id (use with toast.dismiss). |
| toast.success / .error / .warning / .info | (message, options?) | — | Tone-tinted toasts. Wire the Phosphor icon automatically via the Toaster icons prop. |
| toast.promise(fn, { loading, success, error }) | (promise, { loading, success, error }) | — | Fires the loading toast immediately, then swaps to success or error when the promise resolves. Handles the async lifecycle for you. |
| options.description | ReactNode | — | Second line under the title. Rendered muted-foreground; use for context (file name, next step). |
| options.action | { label: string; onClick: () => void } | — | Primary action button (Undo / Retry / Download again). Auto-styled as bg-primary text-xs h-6. |
| options.cancel | { label: string; onClick?: () => void } | — | Secondary Cancel button. Auto-styled as bg-secondary text-xs h-6. |
| options.duration | number | Infinity | 5000 | Milliseconds before auto-dismiss. Pass Infinity for Sonner / Notification recipes the user must dismiss themselves. |
| options.position | "bottom-center" | "top-right" | … | "bottom-center" | Overrides the default Toaster position for this toast. Use 'top-right' for the Sonner / Notification variant — Yuno canon. |
| options.closeButton | boolean | false | Renders the top-right X. Yuno enables this on every Sonner / Notification recipe. |
| options.id | string | number | — | Assign a stable id to update or coalesce a toast (toast(msg, { id: 'export-123' }) fired twice replaces the first). |
| options.icon | ReactNode | — | Override the default tone icon for this toast. Rarely needed — the Toaster icons prop already wires Phosphor. |
| toast.dismiss(id?) | (id?: string | number) => void | — | Dismiss a single toast by id, or dismiss all if no id is passed. |
When to use
- Confirm a successful action ('Rule created successfully.').
- Show a non-blocking error the user can dismiss or retry.
- Offer an Undo affordance for reversible mutations (delete, archive, remove).
- Long-running background operations with progress (export, upload, sync) — use the Sonner / Notification variant.
- Contextual FYIs the user doesn't need to acknowledge (a report is ready, a webhook fired).
When not to use
- The user MUST acknowledge — use AlertDialog.
- Persistent status the user needs to see later — use Alert (inline banner) or Badge.
- Form-field errors — inline error text under the input, not a toast.
- Feature onboarding / tutorials — use a Popover.
- Notifications the user should be able to review later — use a notifications tray (Popover on the bell).
Usage
- Follow the Yuno copy convention: verb + noun + 'successfully' + period.
- Keep the primary message under 60 characters so it never wraps.
- Include an Undo action for every reversible mutation.
- Use the Sonner / Notification variant (duration:Infinity + close X) for anything > 5s.
- Wire the Progress bar to real progress — never fake it.
- Use Phosphor weight=light — the kit's Toaster already sets this via icons prop.
- Don't fire multiple toasts for the same event — coalesce them.
- Don't put required actions inside a default toast — it auto-dismisses. Use Notification (duration:Infinity) for anything the user must decide.
- Don't rely on toast for error states that block progress — those belong inline.
- Don't stack colored borders / tinted backgrounds by tone — Yuno keeps the surface neutral, tone lives only in the icon color.
- Don't change the default position of regular toasts — bottom-center is the Yuno canon. Notifications override to top-right per-toast via toast(..., { position: 'top-right' }); nothing else moves.
- Don't mount more than one <Toaster /> — Sonner is a singleton.
Related
Cross-links to atoms and patterns you may reach for next.
- AlertInline banner for persistent status. Toast is transient; Alert stays until dismissed or the state clears.
- Alert dialogBlocking confirmation the user MUST acknowledge — use for irreversible or destructive decisions.
- CalloutContextual FYI inside a page. Toast is transient; Callout stays with the surrounding content.
- ButtonPowers the action + cancel buttons inside a toast (h-6 primary or secondary).
- ProgressUsed inside the Sonner / Notification recipe for long-running operations.
- IconographyPhosphor weight=light for CheckCircle / Info / Warning / WarningCircle / CircleNotch.