Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Error Toast Notification Block for React

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.

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

Say plainly that a payment failed, and keep saying it until someone acts, with this error toast for React with Base UI. It's the charge page of Chargepath, a payments dashboard, drawn in greys inside a hairline frame with a plus mark on each corner: a top bar, an order with its lines and total, and a card box. The real Charge Shine Button sits on the top bar, a foot strip holds the last request id with a Copy Button, and an error toast is drawn at the foot end, wearing the look of the catalog's Toast: a destructive icon, "Payment failed", the card that was declined and its code, a Retry and a Dismiss. It never times out. Retry counts the attempts, "Attempt 2 of 3: still failing", and on the third the action turns into Contact support. Press Charge and the block raises a real toast through its own toaster, with the same text, at the end of the screen. It's built from Toast, Button, Copy Button, Shine Button and Guides, with CSS motion only. For checkouts, billing pages and any action that can fail and be tried again.

FAQ

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

So you can see an error toast without failing a payment: on load it's drawn in place at the stage's foot end, using the catalog toast's own classes, and its Retry works. Charge raises a real toast and the drawn one stays gone until a reload. In your app keep the toaster and the raise call, and drop the drawn copy: the "in-place" state, DrawnToast and the ol in the frame.

How is the real toast raised, and why doesn't it time out?

The block makes its own manager with createToaster() (Base UI's createToastManager), renders <Toaster toaster={toaster} /> once, and raises the toast with toaster.add({ id, type: "error", title, description, actionProps: { children, onClick }, timeout: 0, priority: "high" }). A timeout of 0 keeps it up, and priority: "high" has it announced at once.An error with an action must not vanish before it's read, so this one stays until it's dismissed, retried or answered.

What does Retry do?

Here it moves to the next attempt: the toast is replaced by a new one that says "Attempt 2 of 3", then "Attempt 3 of 3", and the third offers Contact support instead of Retry, which says support isn't connected in this demo and closes the toast. The card is never charged and nothing is sent: this demo always declines. In your app call your charge endpoint in answer, raise the success toast when it goes through, and keep the count on your server. Charge starts over from the first attempt.

Why does only the toast that is up answer a press?

Because a toaster can deliver an action twice (the catalog's Ark toast passes the handler to both the toaster and its button), and a retry counted twice would skip an attempt. Each press carries the id of the toast it came from, and the block answers only when that id is the one up. The same check ignores a press from a toast that was already replaced.

What does Copy do?

It puts the request id on the clipboard with the Copy Button and says "Copied request" in the block's status message; if the browser refuses, the button says so. A support team asks for this id first, which is why it's on the page and not only in the toast.

Is it accessible?

The real toast lives in a live region that speaks it once and never takes focus. Its Retry and Dismiss are buttons, and after Retry focus moves to Charge, since the toast that had it is gone. The drawn copy is a list named "Notifications", its Dismiss names the toast, and a polite status message says what changed. The error's icon is the only part that takes the destructive color; the words are foreground, and the code is written out, so color is never the message.

How does it look on a phone?

The card box drops out of the drawing, the order stays, and the drawn toast takes the width of the page minus a margin above the foot strip. The real toast is the library's own: full width at the end of the screen.

  • 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

    A seat management console with one warning toast that follows the count: it appears at 90% of the plan, turns into Seat limit reached at the limit, updates in place as the number moves, and takes itself away when it falls back.

  • Pro

    One failed request from an API gateway: a 502 with the endpoint, upstream, time and request id (with a Copy), tabs for the request, the response and the headers in numbered code windows, and a bounded simulated Retry.

  • Pro

    A messaging app's thread with a message toast that carries a Reply action: it never times out, Reply takes it away and puts the sender in the composer with focus, and Receive a message raises the next of three, up to three at once.

  • Pro

    Payment Method Billing

    Payment and invoices

    Payment methods in app settings: the card on file with an expiry warning, a backup bank account, a button that sets the default, and a dialog that checks and updates the card.

  • Pro

    An invoicing app's invoice with a Send button: one toast follows the request through the toaster's own promise call, Sending, then Invoice sent or Couldn't send with a Retry that succeeds, and a toggle group chooses how the next request ends.