Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Toast Stack Notification Block for React

A post scheduler's publishing queue with an Add a toast button: the toasts overlap in a stack of three, open into a list on hover or focus, and the rest wait their turn.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Keep a burst of toasts readable, with this toast stack for React with Base UI. It's the publishing queue of Skeinpost, a social post scheduler, drawn in greys inside a hairline frame with a plus mark on each corner: a top bar and a week of posts. The real Add a toast Shine Button sits on the top bar and cycles four kinds (success, error, warning and info). At the stage's foot end the stack is drawn in place: three toasts that overlap, each a step behind and smaller than the one in front, wearing the look of the catalog's Toast; hover or focus anywhere on the stack and it opens into a list. A foot band says how many are showing and how many wait ("3 showing, 2 waiting") and holds Dismiss all. Press Add a toast and the block hands the same stack to a real toaster through its own queue: at most three are up, the others wait and move up one at a time as you dismiss. It's built from Toast, Button, Shine Button and Guides, with CSS motion only. For publishing tools, dashboards and any app where saves and errors arrive faster than people read.

FAQ

Why is a stack already on the page, and what do I keep?

So you can see the stack without pressing anything: on load it is drawn in place at the stage's foot end, three toasts with two waiting, using the catalog toast's own classes. Add a toast raises the same toasts through a real toaster and the drawn stack stays gone until a reload. In your app keep the toaster, raise, withdraw and the queue helpers (grow, shut), and drop the drawn copy: DrawnToast, the "in-place" state and the ol.

Why does the block keep its own queue of waiting toasts?

The three toasters disagree about what happens to a fourth toast: one queues it, the others tuck the oldest out of sight. The block keeps the queue itself and never has more than three real toasts, so the cap of three, the waiting count and the order a waiting toast moves up are the same on every library. Dismissing a toast, with its close button or with Dismiss all, moves the next one up.

How is the real toast raised on each library?

The block makes its own manager with createToaster() (Base UI's createToastManager), renders <Toaster toaster={toaster} /> once, and raises a toast with toaster.add({ id, type, title, description, timeout, onClose }). A timeout of 0 keeps a toast until it is closed.The block wraps the call in raise(note), so the rest of the file doesn't care which library is under it.

Do the toasts time out, and what about hover and focus?

Not in this demo: each toast stays until it is dismissed, so the queue only moves when you move it and nothing disappears before it can be read. The real toaster takes a duration (duration or timeout) and pauses it while the pointer or keyboard focus is on the stack, so setting one is a one-line change. Hovering or focusing the stack opens it into a list on every library; the drawn copy does the same with CSS alone.

Is it accessible?

The real toasts live in the library's live region, which speaks each toast once and never takes focus, so the block's own status line stays quiet and only says what the toasts cannot: that a toast was queued instead of shown. Every toast has a close button. The drawn copy is a list named "Notifications" with a Dismiss button per toast that names it; only the front toast's words are visible until the stack opens, and the others are hidden from keyboard and screen readers until then (focusing the front toast opens the stack, so Tab reaches them). The error icon is the only status colour, and with reduced motion nothing slides.

What stays local?

Everything. Nothing is posted: the week is a drawing, the toasts are typed words at the top of the file (SAMPLES), and the block says so in its header. In your app, raise a toast from the response of your own request, call withdraw(id) when its cause goes away, and keep the queue if you want the same cap on every library.

  • Pro

    The settings page of a form builder with its Save settings button: two success toasts are drawn on the page, and pressing Save raises a real toast through the block's own toaster, stacking up to three.

  • Pro

    An invoicing app's list with a delete on every row: the toast says how many are deleted with an Undo, a second delete merges into the same toast and starts its wait again, and Restore deleted on the page does the same as Undo.

  • Pro

    A file transfer app's folder with an Upload button: one toast follows the run, a spinner and 1 of 3 uploaded with a Cancel while it runs, then the same toast becomes 3 files uploaded with an Open folder, or Upload cancelled.

  • Pro

    A payments dashboard's charge page with a declined card: an error toast that never times out, a Retry that counts the attempts until the third turns it into Contact support, and the request id with a Copy button.

  • Pro

    Three reminders from a scheduling assistant as a deck of cards: Dismiss lets the next one come forward, Show all fans the deck into a list, and an empty deck says all caught up with a Reset.

  • Pro

    A visitor's message floating on a pricing page: a toggle group parks the card in any of four corners, Reply swaps its buttons for a one-line field, and Dismiss and Show again replay the slide-in.