Toaster

Toast notifications powered by sonner. The <Toaster> component is already mounted in the root layout with design-system tokens and themed icons. Trigger toasts anywhere via the toast() function from sonner.

Anatomy

Mount <Toaster richColors />once in your root layout. The wrapper overrides Sonner's internal CSS variables with design-system tokens — normal background, text, border, and border-radius — so all toasts automatically follow the brand palette and respect dark mode.

Variants

Five semantic variants, each mapped to a custom Lucide icon registered on the Toaster instance. richColors is enabled globally so success/error/warning/info toasts use accessible color schemes.

Semantic types

Action Button

Pass an action object to add a high-emphasis inline button — useful for undo flows. Use toast.promise() for async operations that should automatically transition through loading, success, and error states.

Cancel Button

The cancel option adds a secondary dismiss button styled with a muted background. Unlike action, it signals a non-destructive exit — use it alongside action when the user needs an explicit opt-out.

Loading

Use toast.loading() to show a persistent spinner toast. Every toast() call returns an id — pass it to any subsequent toast call to update the same element in place rather than stacking a new one.

Custom Icon

Override the default icon on any individual toast via the icon option. Pass any React node — a Lucide icon, an avatar, or a status indicator. Pass null to suppress the icon entirely.

Close Button

Add a visible × button to a toast using the closeButton option. Most useful on persistent toasts (duration: Infinity) where auto-dismiss is disabled and the user must act explicitly.

Custom Toast

toast.custom() renders arbitrary JSX with no Sonner styling applied — you own every pixel. The render function receives the toast id so you can dismiss it programmatically from within the content.

Dismiss

Call toast.dismiss(id) to remove a specific toast by its returned ID. Call toast.dismiss() with no argument to clear every active toast at once.

Duration

Control how long a toast stays on screen with the duration option (milliseconds). The default is 4000ms. Pass Infinity for a toast that never auto-dismisses — combine it with closeButton or a custom dismiss action.

Duration examples

Design Guidelines

Do

  • Match the variant to the semantic weight. Use success for completed actions, error for failures that need attention.
  • Keep descriptions short. One sentence maximum — toasts are transient and users won't read a paragraph.
  • Use toast.promise() for async operations. It automatically handles all three states and avoids manual state management.
  • Use cancel alongside action for reversible decisions. Give users a low-friction escape path when confirming intent.

Don't

  • Don't use for critical errors. If a user needs to act on an error, use an inline Alert or Dialog — not a transient toast.
  • Don't spam toasts. Multiple rapid toasts confuse users and create visual noise. Deduplicate or throttle them.
  • Don't put complex content in styled toasts. Long messages, lists, or interactive forms belong in a Dialog or Sheet — or use toast.custom().

Developer Reference

Setup & Accessibility

  • Mount <Toaster richColors /> once in app/layout.tsx. Already configured in this project.
  • Toasts are announced to screen readers via a live region — aria-live="polite" for non-error toasts, aria-live="assertive" for errors.
  • Every toast() call returns an id: string | number. Pass it back to any toast() call or toast.dismiss(id) to update or remove that specific toast.

Key Options

OptionTypeNotes
descriptionReactNodeSecondary line below the title.
durationnumberMs until auto-dismiss. Default 4000. Infinity disables.
action{ label, onClick }High-emphasis inline button (primary style).
cancel{ label, onClick }Low-emphasis dismiss button (muted style).
iconReactNode | nullOverrides the default icon. null hides it.
closeButtonbooleanShows a × button. Most useful with Infinity duration.
idstring | numberTargets an existing toast to update it in place.
dismissiblebooleanWhether the toast can be swiped away. Default true.

Usage

import { toast } from "sonner"
// <Toaster richColors /> is already in app/layout.tsx

// Basic
toast("Saved successfully.")

// With description
toast("File deleted", {
  description: "The file has been moved to trash.",
})

// Semantic variants
toast.success("Changes saved!")
toast.error("Failed to save.", { description: "Please try again." })
toast.warning("Unsaved changes will be lost.")
toast.info("A new version is available.")

// Action button (primary style)
toast("Item deleted", {
  action: { label: "Undo", onClick: () => toast.success("Restored!") },
})

// Cancel button (muted style) alongside action
toast("Save changes?", {
  action: { label: "Save", onClick: () => toast.success("Saved.") },
  cancel: { label: "Discard", onClick: () => {} },
})

// Promise (loading → success/error automatically)
toast.promise(saveData(), {
  loading: "Saving...",
  success: "Saved!",
  error: "Failed to save.",
})

// Loading → update in place via id
const id = toast.loading("Uploading...")
await upload()
toast.success("Upload complete!", { id })

// Per-toast custom icon (pass null to hide)
toast("New message", { icon: <MailIcon className="size-4 text-primary" /> })
toast("No icon", { icon: null })

// Close button (useful with persistent toasts)
toast("Update available", {
  duration: Infinity,
  closeButton: true,
  action: { label: "Install", onClick: () => {} },
})

// Custom duration
toast("Quick note", { duration: 1000 })     // 1 second
toast("Persistent",  { duration: Infinity }) // never auto-dismisses

// Custom JSX toast — full control, no Sonner styling applied
toast.custom((id) => (
  <div className="rounded-lg border border-border bg-card p-4 shadow-sm">
    <p className="text-sm font-medium">Custom content</p>
    <button onClick={() => toast.dismiss(id)}>Dismiss</button>
  </div>
))

// Dismiss
toast.dismiss(id)   // remove specific toast
toast.dismiss()     // remove all active toasts