Toast Stack React component
A stacked toast notification system: toasts pile up in a layered stack that fans out on hover, with six tones including a loading state that turns into a result, auto-dismiss that pauses on hover, swipe to dismiss and an action button.
About the Toast Stack component
Toast Stack shows short notifications that pile up neatly in a corner of the screen. New toasts slide in on top and older ones tuck behind them as a stacked card, and hovering or tabbing into the stack fans them out so each can be read. Each toast can have a title, a description, an icon for its tone, an action button, a close button and a thin bar that counts down until it closes itself; a sideways swipe dismisses one.
The useToastStack hook holds the toasts and gives you push, which adds a toast and returns its id, update, dismiss and clear. Render ToastStack once with its toasts and dismiss. tone picks neutral, success, error, warning, info or loading, and a loading toast stays until you update it to another tone. position picks one of six corners and edges, max limits how many show at once, expand keeps it fanned out with "always", and duration sets the default time before toasts close.
When to use it
Use Toast Stack for short, non-blocking feedback after something happens: a save, a payment, an upload, a failed sync. Several toasts in a row stay tidy because they stack into one card and only fan out when someone hovers or tabs into them.
Use a loading toast for work that takes a moment and update() it to success or error when it finishes, so the same toast carries the whole story instead of two separate ones.
Use a Modal or an inline Callout instead when the message needs a decision or has to stay on screen next to the thing it's about.
Installation
Add the Toast Stack component to your project with the shadcn CLI. It lands in components/toast-stack.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/toast-stackOr copy the source by hand and install its dependencies:
npm install motion clsx tailwind-mergeProps
<ToastStack /> accepts 13 props (required ones are marked *).
| Prop | Type | Default |
|---|---|---|
| toasts* | ToastItem[] | — |
| onDismiss* | (id: string) => void | — |
| position | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "bottom-right" |
| max How many toasts show in the collapsed stack; older ones wait behind until the front ones close. | number | 3 |
| expand "hover" fans the stack out while the pointer (or keyboard focus) is on it; "always" keeps it open. | "hover" | "always" | "hover" |
| duration Default auto-dismiss time for toasts that don't set their own. | number | 4000 |
| showProgress | boolean | true |
| showClose | boolean | true |
| size | "sm" | "md" | "lg" | "md" |
| theme | "dark" | "light" | "dark" |
| contained Positions the stack inside its nearest positioned ancestor instead of the viewport. | boolean | false |
| offset Distance from the edges, in px. | number | 24 |
- toasts*ToastItem[]
- onDismiss*(id: string) => void
- position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"Default: "bottom-right"
- max
How many toasts show in the collapsed stack; older ones wait behind until the front ones close.
numberDefault: 3 - expand
"hover" fans the stack out while the pointer (or keyboard focus) is on it; "always" keeps it open.
"hover" | "always"Default: "hover" - duration
Default auto-dismiss time for toasts that don't set their own.
numberDefault: 4000 - showProgressbooleanDefault: true
- showClosebooleanDefault: true
- size"sm" | "md" | "lg"Default: "md"
- theme"dark" | "light"Default: "dark"
- contained
Positions the stack inside its nearest positioned ancestor instead of the viewport.
booleanDefault: false - offset
Distance from the edges, in px.
numberDefault: 24
Show all 13 propsShow fewer
| className | string | — |
- classNamestring
npm dependencies
Installed for you by the shadcn CLI.
Toast Stack FAQ
- Where should I render the stack?
- Once, near the root of your app, fed by a single useToastStack(). Pass push/update down (or through context) to whatever needs to show a toast.
- How do I keep a toast open until it's dismissed?
- Give it duration: 0. Loading toasts also stay open until you update() them to another tone.
- Does the countdown pause?
- Yes. Hovering or keyboard-focusing the stack pauses every toast's timer, and so does switching to another tab.
- Can people dismiss toasts on touch screens?
- Yes. Swipe a toast sideways to dismiss it, or tap its close button.
- How do I show a loading toast that turns into a result?
- Push it with tone: "loading", keep the returned id, and when the work finishes call update(id, { tone: "success", title: "Saved" }). The same toast changes in place and its countdown starts from then.