Sonner
Sonner shows users the result of an action in a toast that appears in a corner of the screen and disappears by itself.
When to Use
- to confirm an action that has finished, for example “Invoice sent” or “Payment received”
- to let users reverse what they did, with an action like “Undo” next to “Invoice sent”
- to follow a request in one toast with
toast.promise, from “Sending the invoice” to “Invoice sent”
When Not to Use
- for a status that lasts as long as its cause (use an Alert)
- for a decision that users have to make before an action runs (use an Alert Dialog)
- for an error in one field of a form (use the
FieldErrorof a Field) - for the progress of a long task (use the Progress component)
See Alert for a comparison of the Alert, the toast, and the Alert Dialog.
A toast disappears after 4 seconds, and an Alert stays until the page removes it.
Anatomy
| Part | Usage | Content |
|---|---|---|
| Icon | Optional | The icon of the type. A plain toast has no icon. |
| Title | Required | The first argument of toast() |
| Description | Optional | description |
| Cancel button | Optional | cancel, as a ghost Button |
| Action button | Optional | action, as an outline Button |
| Close button | Always shown | An X icon, with its name from labels.toast.close |
The parts are placed in one row, in this order. See Toast for a figure of the same layout.
Types
| Method | Purpose |
|---|---|
toast() or toast.message() | A plain result, such as “INV-2026-014 was sent” |
toast.success() | A good outcome, such as “Payment received” |
toast.info() | News, such as “A new statement is ready” |
toast.warning() | A result that needs attention |
toast.error() | A failure, such as “The payment failed” |
toast.loading() | A task that is running, such as “Sending the invoice” |
toast.promise() | A request, from its loading toast to a success or an error toast |
The type sets only the icon. See Toast for the icon of each type.
Behavior
Toaster renders the stack, and toast() adds a toast to it from anywhere in the app. When a toast appears, focus doesn’t move, and the page stays interactive.
Duration
| Toast | Duration |
|---|---|
toast() and the typed methods | 4 seconds |
toast.loading() | Until it is closed or updated |
toast.promise() while it is pending | Until the promise settles |
toast.promise() once it has settled | 4 seconds, or duration if it is passed |
Any toast with duration | That many milliseconds. If duration is Infinity, the toast stays until it is closed. |
The timer is paused while the pointer is over the stack, while the stack is expanded with Alt+T, and while the browser tab is hidden.
Dismissal
When users click the close button, the action, or the cancel button, the toast is dismissed. The action and the cancel button run their onClick first. Users can also dismiss a toast by swiping it toward an edge that the stack is docked to, which is right or down at the default position.
Stack
By default, the stack shows three toasts, with the newest in front, and a fourth toast hides the oldest. When the pointer is over the stack, or when users press Alt+T, the stack expands, and all of its toasts are shown in full.
Position and Size
By default, the stack is positioned at the bottom right. To move it, set position on Toaster. See Layout for the width of a toast.
If a title or a description is long, it wraps, and the toast grows taller. A toast is shown over a Dialog and its overlay. See Elevation for the list of layers.
In a right-to-left layout, Toaster mirrors the content of the toasts, but the stack stays at the bottom right. If you want it at the bottom left, pass position="bottom-left". See Right-to-Left for the other parts that don’t mirror.
Content
| String | Rule | Example | Counterexample |
|---|---|---|---|
| Title | Say what happened, with no period | “Invoice sent” | “Success” |
| Description | Add one sentence on what follows | “Atelier Brume will get it by email.” | “Your invoice was sent successfully.” |
| Action | Use one verb | “Undo” | “Click here to undo” |
| Loading title | Name the task that is running | “Sending the invoice” | “Please wait” |
The name of the close button is labels.toast.close, which is “Close toast” by default. If no loading title is passed to toast.promise, its loading toast shows labels.toast.loading, which is “Loading…”. To translate both, use the Summit Provider.
Accessibility
Sonner announces a toast through a live region with aria-live="polite", so a screen reader reads a new toast without interrupting what it is saying. The name of the region begins with “Notifications”. To replace that word, set containerAriaLabel on Toaster.
Keyboard users press Alt+T to move focus to the stack and expand it, and Escape to collapse it. If you want to change the shortcut, set hotkey on Toaster.
Requirements
A toast must not be the only copy of a message or the only way to an action because it disappears after 4 seconds,
before some users have read it. Keep the record in the page, or pass duration: Infinity. W3C’s guidance for
WCAG 2.2 SC 2.2.1 Timing Adjustable counts a message that disappears as a time limit unless the same information can be found
another way.
The title must state the outcome in words, for example “The payment failed”, so that screen reader users can tell a failure from a success. The type is shown only as an icon, which is hidden from assistive technology, and WCAG 2.2 SC 1.1.1 Non-text Content requires text with the same information.
Installation
npx shadcn@latest add @summit/sonnerThe CLI also adds @summit/button, @summit/summit-provider, and @summit/toast-icon.
Usage
Render Toaster once, in the root layout. Call toast from any event handler.
import { Button } from '@/components/ui/button';
import { toast, Toaster } from '@/components/ui/sonner';
<>
<Button variant="outline" onClick={() => toast('Invoice sent')}>
Send invoice
</Button>
<Toaster />
</>;With an Action
To add a line under the title, pass description. To add an action, pass action with a label and an onClick. When users click the action, the toast is dismissed.
toast('Invoice sent', {
description: 'Atelier Brume will get it by email.',
action: { label: 'Undo', onClick: () => unsend(invoice) },
});Show a Type
To show a type, call the method that matches the outcome.
toast.success('Payment received');
toast.error('The payment failed', { description: 'The card was declined.' });Follow a Request
To follow a request, pass its promise to toast.promise. The toast shows loading until the promise settles, and then success or error. The settled toast is closed after 4 seconds, or after duration if you pass one.
toast.promise(sendInvoice(invoice), {
loading: 'Sending the invoice',
success: 'Invoice sent',
error: 'The invoice was not sent',
duration: 4000,
});Replace or Dismiss a Toast
toast() returns an id. To replace a toast, pass that id to another call; the toast keeps its duration unless the call passes a new one. To remove one toast, pass its id to toast.dismiss. If you call toast.dismiss with no id, all toasts are removed.
const id = toast.loading('Exporting 48 invoices', { duration: Infinity });
toast.success('Export ready', { id, duration: 4000 });
toast.dismiss(id);Move the Stack
To move the stack, set position on Toaster.
<Toaster position="top-center" />API Reference
Options and props that aren’t listed here are passed to Sonner.
toast
toast(title, options) returns the id of the toast. toast.message, toast.success, toast.info, toast.warning, toast.error, and toast.loading accept the same arguments.
| Option | Type | Default | Description |
|---|---|---|---|
description | ReactNode | None | A line under the title |
action | { label, onClick } or a ReactNode | None | An outline Button. onClick runs, and then the toast is dismissed. |
cancel | { label, onClick } or a ReactNode | None | A ghost Button before the action. It dismisses the toast in the same way. |
duration | number | 4000 | The time in milliseconds before the toast disappears |
id | string | number | Generated | Replaces the toast that has this id |
See the Sonner toast documentation for position, dismissible, onDismiss, and onAutoClose.
toast.custom, toast.dismiss, toast.getHistory, and toast.getToasts are Sonner’s own functions. toast.custom renders its JSX without Summit’s parts.
toast.promise
toast.promise(promise, options) accepts a promise or a function that returns one. It returns the id and unwrap, a function that returns the promise.
| Option | Type | Default | Description |
|---|---|---|---|
loading | ReactNode | labels.toast.loading | The title while the promise is pending |
success | ReactNode, { message, description }, or a function of the result that returns either | None | The toast shown when the promise resolves. If it isn’t set, the toast is dismissed. |
error | ReactNode, { message, description }, or a function of the error that returns either | None | The toast shown when the promise rejects. If it isn’t set, the toast is dismissed. |
description | ReactNode | None | The line under the title of each toast unless success or error returns its own |
finally | () => void | Promise<void> | None | Runs after the promise settles |
Toaster
| Prop | Type | Default | Description |
|---|---|---|---|
position | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'bottom-right' | The position of the stack |
Toaster passes its theme from next-themes to Sonner. See the Sonner Toaster documentation for expand, visibleToasts, offset, hotkey, gap, dir, and toastOptions.