Migrate from Sonner
import { toast, Toaster } from 'sonner';import { toast, Toaster } from 'scrollsheet';Drop-in: same toast, useSonner, Toaster exports, built on one non-modal Sheet.Root per
<Toaster> instead of Sonner’s own DOM stack. toast(), .success(), .error(), .info(),
.warning(), .loading(), .message(), .custom(jsx), .promise(), .dismiss(id?) all work
before or after any <Toaster> mounts, backed by the same module-level queue. A repeated id
updates a toast in place instead of stacking a second one. toasterId routes independently: a
toast() call with a matching toasterId shows only on that instance, everything else on the
default un-keyed one.
The stack shows a collapsed front card with ghost cards behind it, expanding to the full list on
hover or focus, same as real Sonner. Auto-dismiss uses the toast’s own duration, or the
Toaster’s. Bug fixed along the way: Sheet.Content moves focus onto the panel by default,
which would steal focus every time a toast fired. This shim captures and restores focus around
the mount so it never does.
Fire it a few times in a row and watch the queue stack, newest on top.
<Toaster visibleToasts={5}> caps how many toasts render at once, default 3 like Sonner’s own.
Past the cap, the oldest dismissible toast evicts (onDismiss fires, same as a manual close) to
make room for the new arrival. A dismissible: false toast holds its slot instead: if every
visible slot is pinned that way, a new toast queues untouched in the store until one clears
itself. visibleToasts clamps to a minimum of 1: 0 or a negative number still shows one toast,
not zero.
Enter/exit, per-row swipe-to-dismiss (drag past 45px), and the crossfade between the collapsed
and expanded stack are CSS transitions, not a JS animation loop, adapted from Sonner’s own
approach. prefers-reduced-motion: reduce collapses every duration to zero and turns the
transitions off outright, the same contract the core primitive already keeps.
Auto-injected styles land in document.head on first render, same as the core primitives.
Import scrollsheet/sonner.css yourself to control load timing instead.