Sonner
Shows brief, non-blocking messages, like a saved record or a failed request.
Installation
The CLI installs the Radix or Base UI file to match your project.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- To confirm that something happened, like a saved record or a sent email, without interrupting people.
- To report a background failure people should know about, like a request that didn't go through.
- For short-lived news that needs no answer, or one optional action, like Undo.
Don't use it
- When the message belongs to one part of the page, like a form's errors. Use Alert next to that part, or the field's own error message.
- When people must read it or decide something before they go on. Use Dialog.
- When the message must stay until the problem is fixed, like a lost connection. Use Alert with
variant="banner". - When the text is long or has several actions. Toasts are for one or two short lines.
Usage
Render one Toaster near the root of the app, then call toast() from an event handler anywhere. The file exports toast too, so both come from one place.
import { Toaster, toast } from "@/components/ui/sonner"Once, near the root of the app:
<Toaster />Then, in an event handler:
toast.success("Email sent.")A link in the title or the description takes the toast's text color, underlined. Put a link people need in the title: phones show only the title.
For one button in the toast, like Undo, pass action: { label, onClick }; the toast runs onClick and then closes. A fixed id replaces a toast instead of stacking a second one.
Examples
Types
Buttons that call `toast.success()`, `.info()`, `.warning()` and `.error()`, with `duration: Infinity` on the warning and error so they stay until closed.
Record created
Save calls `toast.success()` with the new contact's name linked in the title, a description, and `duration: 10000` so there's time to reach the link.
API reference
| Part | Built on |
|---|---|
Toaster | Sonner (opens in a new tab) |
toast | Sonner toast (opens in a new tab) |
Toaster
| Prop | Type | Default |
|---|---|---|
position | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center" | "top-center" |
expand | boolean | true |
closeButton | boolean | true |
SLDS mapping
Toaster is slds-notify_container. Each toast is slds-notify slds-notify_toast with the theme class from the table below. The title is slds-text-heading_small, the icon sits in slds-icon_container, and the close button is slds-notify__close.
Known gaps:
- SLDS toasts have no buttons. An action button looks like a neutral button, and a cancel button like a link in the toast's text color.
- Every toast has one width, so toasts don't grow with their text. Pass
style={{ "--width": "…" }}toToasterto change it. - Toasts sit further from the top of the screen and further apart when listed than SLDS's.
- Icons are lucide's, not SLDS's utility icons.
| Prop | Value | SLDS | Notes |
|---|---|---|---|
toast() | default | slds-notify_toast slds-theme_info | SLDS's base toast is the info toast. |
toast.info() | info | slds-notify_toast slds-theme_info | |
toast.success() | success | slds-notify_toast slds-theme_success | |
toast.warning() | warning | slds-notify_toast slds-theme_warning | |
toast.error() | error | slds-notify_toast slds-theme_error | |
toast.loading() | loading | slds-notify_toast slds-theme_info | SLDS has no loading toast. Info colors with a spinning icon. |
Keyboard interactions
| Keys | Action |
|---|---|
| Alt+T | Lists the toasts and moves focus to them. Change the keys with hotkey. |
| Escape | With focus in the toasts, stacks them again. |
Accessibility
- Keep toasts short. Screen readers read the whole toast, and a toast without
duration: Infinitydisappears after four seconds. - Don't put the only way to do something in a toast; it can time out before people reach it. Repeat the action on the page.
- Toasts don't take focus when they appear. Someone who misses one gets it back with Alt+T.
Motion
- Toasts slide in from the top and fade out, in every theme. The loading icon spins.
- With reduced motion the slide and fade are off, and the loading icon holds still.
Responsive behavior
- Below 600px toasts fill the screen, less 16px on each side.
- Below 30em (480px) toasts show only their title, as SLDS's small-screen toasts do: the icon and the description are hidden. The title must carry the message on phones, links included.
- Three toasts show at a time, listed with the newest on top, as SLDS lists them. Pass
visibleToaststo show more, orexpand={false}to stack them behind the newest until hovered.
Rules
- Render exactly one
Toaster. EveryToastershows every toast, so a second one shows each toast twice. - Call
toast()from an event handler or after a request finishes, never while rendering. Each render would show another toast. - Pick the type by outcome:
toast.success()for done,toast.error()for failed,toast.warning()for needs attention soon,toast()ortoast.info()for news. - Pass
duration: Infinitytotoast.error()andtoast.warning(), so they stay until closed, as Salesforce's do. A toast with a link takesduration: 10000, so people have time to reach the link. - Pass options like
durationto eachtoast()call.toastOptionsonToasterreplaces the classes that give toasts their look in each theme. - Don't style toast colors with classes. The theme sets them, and they change per theme.
- Don't turn off the close button unless every toast times out.