shadcn.io is not affiliated with official shadcn/ui
Promise Toast Notification Block for Solid
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 Solid with Ark 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({ max: 3, overlap: true, placement: "bottom-end" }), renders <Toaster toaster={toaster} /> once, and calls toaster.promise(job, { loading: { title }, success: { title, description }, error: { title, description, action: { label: "Retry", onClick }, duration: Infinity } }). It makes the toast with a loading type and calls create again with the same id for the answer, so one toast changes in place.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".