Toaster

Announce ephemeral, non-blocking feedback after an action — saved, copied, deleted, failed — from anywhere in the tree.

Toaster is a thin wrapper around Sonner wired up with the product’s theme tokens. It ships as two pieces that work together: <Toaster /> is a component you mount once near the root of the app, and toast(...) (plus toast.success, toast.error, toast.info, toast.warning, toast.loading, toast.promise) is a function you import from @unkey/ui and call from anywhere — event handlers, data hooks, server-action responses — to make a toast appear in that single mounted region.

Reach for a toast when feedback is ephemeral and non-blocking: “saved”, “copied”, “deleted” (with an undo), “could not reach the server”. Use an <Alert> instead when the message is inline and persistent (a deprecated-API banner, a read-only-mode notice). Use a <Dialog> when the user must acknowledge or confirm before continuing. Toasts auto-dismiss, overlap other content, and can be missed, so they should never be the only record of a critical event.

Mounting

Mount <Toaster /> exactly once, as high in the tree as makes sense (the root layout or a top-level provider). Every toast(...) call on the page targets this single instance, and mounting it twice causes toasts to appear in both regions.

import { Toaster } from "@unkey/ui";

export function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster position="bottom-right" richColors />
      </body>
    </html>
  );
}

The design system site does not mount a global <Toaster />, so each example on this page mounts its own local <Toaster /> inside _examples.tsx alongside the trigger button. That is only a documentation workaround — in a real app, mount it once.

Import

import { Toaster, toast } from "@unkey/ui";

Toaster is the component you render. toast is the imperative function (with .success, .error, .info, .warning, .loading, .promise, .dismiss, .message, .custom attached) you call to show a message.

Default toast

Call toast(message) for a neutral notification. No icon, no colored border — use this for informational confirmations that don’t need semantic weight.

Success

toast.success(message) adds a success icon and (when richColors is on) green theming. Use it for confirmations of positive user actions: a key rotated, an invitation sent, a configuration saved.

Error

toast.error(message, options) flags a failure. Pair with a description that tells the user what to do next. Error toasts auto-dismiss just like any other; if the user needs to read and act on the failure, consider an <Alert> in the affected surface instead.

With description

Pass a description in the options object for a second line of body copy. Keep the title short (the “what”); put supporting detail in the description (the “who”, “where”, or “when”).

With action

Pass an action with a label and an onClick to put an inline button inside the toast. The classic use is an undo affordance after a destructive operation: the destructive work runs optimistically, and the toast gives the user a short window to reverse it before dismissal.

Accessibility

Sonner renders toasts into an aria-live region (polite by default), so screen readers announce new toasts without interrupting the current announcement. The region also carries a configurable label (containerAriaLabel, default "Notifications") so assistive tech can name it when a user jumps to landmarks.

Two rules keep toast usage honest. First, because toasts auto-dismiss and can be missed entirely (a user on another tab, a screen reader user who stepped away), never let a toast be the only record of a failure that blocks further work — duplicate the state in the affected UI. Second, the hotkey (default Alt+T) focuses the toast region so keyboard users can reach any action button inside; pressing Escape from there dismisses the focused toast.

Props

<Toaster>

position "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center" Optional Default "bottom-right"

Which corner of the viewport the stack anchors to. Bottom-right is the dashboard default; top-center is common for marketing or onboarding flows where the user’s eye is already near the top.

richColors boolean Optional Default false

When true, success, error, warning, and info toasts render on semantically tinted backgrounds. Off by default so toasts stay visually quiet; turn on per-app if you want the extra signal.

expand boolean Optional Default false

When true, stacked toasts are all fully visible instead of collapsing behind the topmost one. Useful when multiple notifications fire in rapid succession and the user should see all of them.

duration number Optional Default 4000

Default milliseconds before a toast auto-dismisses. Override per toast by passing duration in the toast(...) options. Use Infinity to require an explicit dismiss (pair with closeButton).

visibleToasts number Optional Default 3

How many toasts are visible at once; additional toasts queue and surface as older ones dismiss.

closeButton boolean Optional Default false

When true, every toast renders a close (x) affordance. Recommended when duration is very long or Infinity.

theme "light" | "dark" | "system" Optional Default "system"

Follows next-themes automatically; override only when you need to pin a specific theme for a subtree.

hotkey string[] Optional Default ["altKey", "KeyT"]

Key combination that focuses the toast region so keyboard users can reach action buttons. Provide as an array of KeyboardEvent property names.

offset string | number | Offset Optional

Pixel offset from the chosen position. Pass a single value to apply to both axes, or an object like { top, right, bottom, left } to target individual edges.

toastOptions ToastOptions Optional

Default options merged into every toast. Our wrapper already wires the product’s bg-background, text-content, and border-border tokens here; override sparingly.

containerAriaLabel string Optional Default "Notifications"

Accessible name for the live region.

toast(...)

The imperative entry point. Every variant returns a toast id you can pass to toast.dismiss(id) to dismiss early.

toast(message, options?)
toast.success(message, options?)
toast.error(message, options?)
toast.info(message, options?)
toast.warning(message, options?)
toast.loading(message, options?)
toast.message(message, options?)
toast.promise(promise, { loading, success, error })
toast.custom((id) => <MyToast id={id} />, options?)
toast.dismiss(id?)
message ReactNode Optional

The title of the toast. Keep it short — one line, active voice (“Saved”, “Key rotated”, “Invite sent”). Detail goes in description.

options.description ReactNode Optional

A second line of body copy rendered beneath the title in muted color.

options.action { label: ReactNode; onClick: (event) => void } Optional

An inline button inside the toast. Classic use is undo after a destructive action; the onClick receives the mouse event and can call toast.dismiss(id) if you want to close immediately on click.

options.cancel { label: ReactNode; onClick: (event) => void } Optional

A secondary inline button styled as a cancel affordance.

options.duration number Optional

Per-toast override of the Toaster’s default duration, in milliseconds. Infinity keeps the toast until dismissed.

options.id string | number Optional

Stable id for this toast. Calling toast(..., { id }) again with the same id updates the existing toast in place instead of stacking — use this to reflect progress (loading then success on the same id).

options.dismissible boolean Optional Default true

When false, the toast ignores swipe and close-button dismissal, and stays until its duration elapses (or forever, if Infinity).

options.onDismiss (toast) => void Optional

Fires when the user dismisses the toast manually (swipe or close button).

options.onAutoClose (toast) => void Optional

Fires when the toast closes on its own after duration elapses.

  • Alert — inline, persistent region for context that must stay visible while the condition holds.
  • Dialog — blocking confirmation when the user must acknowledge before continuing.