shadcn.io is not affiliated with official shadcn/ui
Promise Toast Notification Block for React
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.
Installation
Overview
Follow a request from the press to the answer in one toast, with this promise toast for React with Base UI. It's invoice 1042 of Tallybarn, an invoicing app, drawn in a hairline frame with a plus mark on each corner: a top bar with the Send invoice Shine Button, the invoice's number, customer and status word, and its lines in greys. Above the page a Toggle Group says how the next request ends, Succeed or Fail. Press Send and the block calls the toaster's own promise with a 1.4 second request: the toast, wearing the look of the catalog's Toast, shows "Sending invoice 1042" and then, in place, becomes "Invoice sent" or "Couldn't send" with a Retry; Retry sends again and succeeds. The failure never times out, since it carries an action. On load the toast of the previous invoice is drawn in place; Send raises a real one at the end of the screen. It's built from Toast, Toggle Group, Shine Button, Button and Guides, with CSS motion only. For saves, sends, syncs and any call that can take a moment and fail.
FAQ
Why is a toast already on the page, and what do I keep?
So you can see a toast without sending anything: on load the toast of the previous invoice, "Invoice sent", is drawn in place at the stage's foot end, using the catalog toast's own classes. Send invoice raises a real toast and the drawn one stays gone until a reload. In your app keep the toaster and the follow call, and drop the drawn copy: the "in-place" state, DrawnToast and the ol in the frame.
How does the toaster follow a promise?
createToaster() (Base UI's createToastManager), renders <Toaster toaster={toaster} /> once, and calls toaster.promise(job, { loading: { title }, success: { title, description }, error: { title, description, actionProps: { children: "Retry", onClick }, timeout: 0 } }). One toast changes in place, and the promise it returns rejects when the request does, so the block catches it.The page's own status word listens to the same promise, so it can't disagree with the toast.What is the request, and how do I wire it up?
A promise settled by a timer: window.setTimeout 1.4 seconds after the press, resolving or rejecting by the toggle, stored in a ref and cleared when the block leaves the page. Nothing is sent. In your app replace the timer with your request, fetch or a mutation, and keep the rest: the toast needs only a promise.
What does Retry do?
It takes the failed toast away and sends again with a request that succeeds, so the demo shows the way back: a new toast says "Sending invoice 1042" and then "Invoice sent", and focus returns to Send invoice. Only a failed request answers a Retry, once, because a toaster can deliver an action twice. In your app retry with the same request, and keep the failed state on the page for as long as the problem lasts.
Is it accessible?
The real toast lives in a live region that speaks each state as it arrives, and the failure carries a Retry button and stays until it is answered. The toggle group is named "Make the request" and works with the arrow keys; Send invoice is quiet while a request is out and says so if pressed. The status word, "Draft", "Sending", "Sent" or "Not sent", is text, never only color, and the error's icon is the toast's own. The drawn copy is a list named "Notifications".
Related
- 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
Error Toast Notification
ToastsA 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
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 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
Connection Lost Notification
Live and empty statesA whiteboard that keeps working offline: an offline bar docked at the foot with Retry now and its attempt count, the list of edits waiting to send, troubleshooting tips, and Work offline to fold it to a pill.
- Pro
Reconnected Notification
Live and empty statesA co-editor's back online notice drawn at the top centre of a document: it says what you missed, opens a catch-up list grouped by type with counts, and folds after a ten-second drain that holds on hover and focus.