Gesso
Feedback

Toaster

Transient sonner toasts at the bottom center, with status icons that match Alert.

Toaster is the kit's styling for sonner, a stack of short messages that appear at the bottom center of the screen and leave on their own. Mount <Toaster /> once in the app shell and call toast() from anywhere, even outside React. A toast reports what just happened, like a save, and it does not take space in the layout. The page's own, longer-lived messages belong to Alert.

Usage

import { Button } from '@/components/ui/button';
import { Toaster, toast } from '@/components/ui/sonner';

<Toaster />
<Button onClick={() => toast.success('Saved')}>Save</Button>

Status and look

toast() is the plain toast, and toast.success, toast.info, toast.warning, and toast.error add a status. Each status draws a 20px Phosphor fill icon in the same color as the matching Alert. Each status also takes the status tint, the same as an Alert, while the plain toast stays on bg-surface. A toast has no border, lifts with shadow-sm, and takes the look's rounded-control. A toast is as wide as its content, up to min(40rem, 100vw - 2rem).

description adds a line in text-fg-muted beside the title, and both wrap once the toast reaches its maximum width, so a long message stays inside it.

Duration, actions, and pending work

A toast leaves after 4 seconds by default, and duration on the call or on <Toaster /> changes it. Number.POSITIVE_INFINITY keeps it until someone closes it, and closeButton: true on the toast adds a close button. At most three are visible at once, stacked, and pointing at the stack opens it out.

action puts a button after the text, like Undo, in the primary fill. Pressing it runs its onClick and then closes the toast unless the handler calls event.preventDefault(). A cancel takes the same shape in a quieter fill.

toast.promise(promise, { loading, success, error }) shows a spinner toast and swaps it for the result. A loading toast never times out, so it stays until the promise settles. Click Save changes, and open the second toast to see one that stays.

Changing the defaults

<Toaster /> takes every sonner prop and sets these defaults, which your props replace.

PropDefaultWhat it does
position"bottom-center"Where the stack sits. Swipe-to-dismiss follows it, so a bottom toast is swiped down.
duration4000Milliseconds before a toast leaves. Set per toast to override.
closeButtonoffAdds a close button to every toast.
visibleToasts3How many show before the rest wait behind them.
hotkey["altKey", "KeyT"]The shortcut that moves focus to the toasts.

style and toastOptions.classNames are merged with the kit's, and yours win on a conflict.

Accessibility

  • Sonner wraps the toasts in a <section> with aria-live="polite", aria-relevant="additions text", and the name "Notifications alt+T". A screen reader announces each toast when it is added, without moving focus, and does not interrupt what it is reading.
  • The announcement is the text only. A toast has no role of its own and the icon carries no name, so the status is not announced, and the message should say what happened, like "Upload failed".
  • Alt+T expands the stack and moves focus into it, and Escape collapses it again. Focus returns to the element it came from when it leaves the toasts. Each toast and its buttons are focusable, so an action like Undo can be reached by keyboard. While the stack is expanded, hovered, pressed, or in a hidden tab, the timers pause, so a toast does not leave while someone is using it.
  • A toast still leaves after 4 seconds, and a person who needs longer to read it or reach an action has to use the hotkey in time. Give a toast that carries an action or an error a longer duration, or Number.POSITIVE_INFINITY with a close button.
  • Swiping a toast toward the bottom of the screen dismisses it. A toast with dismissible: false ignores the swipe, so it needs another way to go.
  • The action and cancel buttons are text-xs with py-1, about 24px tall, and at least 44px tall on a touch screen. The close button is sonner's 20px circle.
  • Under prefers-reduced-motion sonner turns off the entrance, exit, and stacking transitions, and the kit's loading spinner stops turning, so a loading toast has no motion cue and the text has to say that work is in progress.
  • Forced colors replace the muted fills, and the toast has no border, so there the toast's edge is only its shadow, which forced colors also drops. This has not been checked in a forced-colors browser, so test it if you support Windows high contrast.

When to use something else

  • Alert for a message that should stay on the page until the thing it describes changes. A toast is gone in a few seconds and is easy to miss.
  • Dialog or Alert dialog when a decision has to be made before the person goes on. A toast is never modal and its action is optional.
  • A Field's error for a problem with one input, which Field ties to the control, because a toast is not read with the control.
  • A Button's loading state, from Button, for work that started at that button, and a toast for the result when it finishes somewhere else.

Install

shadcn add @gesso/toaster

On this page