Components / Feedback
ToastStack
Stack short results at the edge until you reach for them.
Install
npx shadcn@latest add https://irisui.dev/r/toast-stack.jsonimport { ToastStack } from "@/components/iris/toast-stack/toast-stack";Preview
ToastStack
A queue of short, typed toasts that rise from the bottom edge, tuck behind each other, and fan out into a list on hover or focus.
When to use
- Non-blocking results of what someone just did, when several can arrive close together: saves, exports, deletes with Undo.
- Long-running work that should morph in place from "loading" to its result.
- Not for a single fixed confirmation:
Toastis lighter. Not for conditions that persist: useAlert.
Usage
import { ToastStack } from "@/components/iris/toast-stack/toast-stack";
// Any component rendered under ToastStackProvider can raise toasts.
function SaveButton() {
const { toast, update } = useToastStack();
return h(
"button",
{
onClick: () => {
const id = toast({ type: "loading", title: "Exporting 2,980 pageviews" });
exportCsv().then(() => update(id, { type: "success", title: "Export ready" }));
},
},
"Export CSV",
);
}
h(ToastStackProvider, { duration: 5000 }, h(SaveButton), h(ToastStack, { position: "bottom-right" }));
Props
ToastStackProvider
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
— | Required. Scope of the queue; two providers never share toasts. |
duration |
number |
5000 |
Base lifetime in ms. Warnings and errors stay 1.6 times longer; loading toasts wait for an update. |
limit |
number |
12 |
Oldest toasts beyond this count are dropped. |
ToastStack (render once inside the provider)
| Prop | Type | Default | Description |
|---|---|---|---|
position |
"bottom-right" | "bottom-center" | "bottom-left" |
"bottom-right" |
Corner of the window (or container). |
contained |
boolean |
false |
Pins the stack inside the nearest positioned ancestor instead of the window. |
visibleToasts |
number |
3 |
How many show at once; older ones wait behind. |
hotkey |
boolean |
true |
Alt+T moves focus into the stack. |
label |
string |
"Notifications" |
Accessible name of the region. |
className |
string |
— | Extra class. |
useToastStack() returns { toast, update, dismiss, count } and must be called inside a ToastStackProvider; count is how many toasts are queued.
Toast options passed to toast(): { id?, type?: "success" \| "info" \| "warning" \| "error" \| "loading", title, description?, action?: { label, onClick(id) }, duration? }. update(id, patch) morphs a toast in place and restarts its timer; dismiss(id?) closes one or all.
Notes
- Calling
useToastStack()outside aToastStackProviderthrows. Each provider scopes its own queue. - Reusing an
idintoast()updates that toast in place instead of stacking a new one. - The region is
aria-live="polite". Timers pause while the stack is hovered, focused, being dragged, or the tab is hidden. Swipe right to dismiss. - Reduced motion replaces the stacking springs with fades.
- Reads
--surface,--hover,--line,--line-strong,--ink,--ink-2,--ink-3,--shadow-raised,--shadow-floating, and the tone tokens--green,--accent,--orange,--red.