SakaniUI

Sonner

A managed toast stack. Mount one Toaster, call toast() from anywhere. Toasts pile up, spread on hover and swipe away.

Use Toaster and toast() when you want toasts triggered from code and managed for you: the queue, position, timer, stacking and swipe-to-dismiss. For a single static card you place yourself, use Toast.

Stacking

Add a few. They pile up with the newest in front; hover the pile to spread it out, then drag the front toast down and let go.

Types

Rich colors

By default types differ only by icon. richColors tints the whole toast with the success, info, warning or danger tokens. Default and loading toasts are never tinted.

Actions and close button

Promise

One toast follows an async task: loading first, then success or error, in the same place.

Updating and dismissing

Position

Six positions, 24px from the viewport edge (16px on phones, where toasts fill the width). Pick one, then add a toast.

Motion

Movement runs 400ms on a gentle ease. With reduced motion on, toasts keep the fades and drop the movement.

MomentWhat happens
A toast arrivesRises in from below its own height while fading in. 400ms.
More toasts arriveOlder toasts lift 14px and shrink 5% per level, with their content hidden so only an edge shows. Three are visible.
Hover or focusThe pile spreads into a list, 14px between toasts, each at its own height. Timers pause.
Pointer leavesThe list folds back into the pile and the timers resume.
SwipeThe toast follows the pointer. Release under 45px and it springs back; past 45px, or a quick flick, it flies out in 200ms. The direction follows the position.
Lifetime4 seconds by default. Loading toasts stay until you update them.

Accessibility

  • Toasts live in a labelled notifications region. Each one is announced politely; errors and important toasts are announced assertively.
  • Press alt + T to focus the stack, Esc to fold it. Focus spreads the pile and pauses the timers, like hover.
  • Every toast can be dismissed with Delete while focused, and the close button and actions are real buttons with focus rings.
  • Don't put anything people must act on in a toast. It disappears after 4 seconds; use Alert or Modal instead.

toast()

CallWhat it does
toast(title, options?)Default toast, no icon. Returns the toast id.
toast.success / info / warning / errorSame signature, with the type's icon (tinted when richColors is on).
toast.loading(title, options?)A toast with a spinner and no timer. Update or dismiss it by id.
toast.promise(promise, messages)Shows messages.loading, then success or error from the same toast. success and error can be functions of the result.
toast.dismiss(id?)Closes one toast, or all of them without an id.

Toast options

PropTypeDefaultDescription
idstring | numberReuse an id to update a toast in place instead of adding a new one.
descriptionReactNodeSecondary line under the title.
iconReactNode | nullReplaces the type icon. null shows no icon.
action{ label, onClick }Primary button, right-most. Closes the toast after onClick unless you call event.preventDefault().
cancel{ label, onClick? }Secondary button, left of the action. Closes the toast.
closeButtonbooleanShows the round close button on this toast.
durationnumber4000Overrides the Toaster's duration. Loading toasts have no timer.
dismissiblebooleantrueWhen false the toast cannot be swiped away or closed by the user.
importantbooleanfalseAnnounces the toast assertively. Errors always are.
richColorsbooleanTints this toast by its type.
onDismiss(toast) => voidCalled when the user closes it (close button, cancel, swipe) or toast.dismiss() is called.
onAutoClose(toast) => voidCalled when the toast closes itself after its duration.
classNamestringExtra class on the toast element.

Toaster props

PropTypeDefaultDescription
position'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right''bottom-right'Where the stack sits. Top positions stack downwards, bottom positions upwards.
richColorsbooleanfalseTints success, info, warning and error toasts by type.
closeButtonbooleanfalseShows the round close button on every toast.
expandbooleanfalseKeeps the stack spread out instead of piled up.
visibleToastsnumber3How many toasts show at once. Older ones fade out.
durationnumber4000Milliseconds before a toast closes itself. Infinity keeps it open.
gapnumber14Space between toasts when spread out, in px.
offsetnumber | string24Distance from the viewport edge. Numbers are px; strings are any CSS length.
mobileOffsetnumber | string16Distance from the edge on screens 600px wide or less, where toasts fill the width.
hotkeystring[]['altKey', 'KeyT']Keys that focus the stack (alt+T).
containerAriaLabelstring'Notifications'Accessible name of the notifications region.

Why it works this way

Why

  • It owns the queue, position, timers and stacking, so the app only says what happened. toast() works from handlers, hooks and plain modules without passing state around.
  • Toasts pile up behind the newest and spread on hover, so a burst of notifications takes the room of one until you look at them.

Don't

  • Don't use a toast for anything people must act on. It disappears after 4 seconds. Use Alert or Modal.
  • Don't mount more than one Toaster. Every toast would show in all of them.
  • Don't render Toast yourself for this. Toast is the static card; Toaster and toast() manage a stack of them.