Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Two Actions Dialog Block for React

A mail client with its sent confirmation shown open: a dialog with a View message button and an Undo whose label counts down from 10 while a fill drains, with Return to drafts staying on the page.

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

Give a person a moment to take a send back, with this two-button confirmation for React with Base UI. It opens from a new message in Heronmail, an email client: the header holds an outline Send Button, and the mail list is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, the message first as a Draft. The Dialog says "Message sent" with who it went to, and its footer holds a pair: Undo, outline, whose label counts down from 10 while a fill drains behind it, and View message, the one Shine Button, which has focus. At zero Undo reads "Undo expired" and stays focusable. The Undo is a shortcut, never the only way back: the header's button turns into Return to drafts as soon as the message is sent and works whenever the person gets to it, so nobody is punished for being slow. The pair stacks on a phone and sits side by side from the small breakpoint up. For preview the dialog is drawn open in place when the page loads, its clock stopped at Undo (10); Send opens the real Dialog, where the clock runs from the moment it opens. For email, chat, scheduled posts and any action that can be taken back.

FAQ

Why is the dialog already open, and why is the clock stopped?

So you can see it without a click, and so a poster or a gallery card never ages. On load the dialog is a copy drawn in place over the mail list: a native dialog element that isn't modal, so nothing is trapped, hidden or focused. In it Undo stands still at "Undo (10)" with its fill full, and a muted line says the clock runs when the dialog is opened from Send. Send in the header opens the real Dialog, modal, with its focus trap and Escape, and there the clock runs. Both wrappers share SentFields and the block's state. In your app keep the Dialog and drop the copy.

How does the countdown work?

The catalog Timer, kept out of sight, counts down from ten seconds and sets the number in the label; the fill is a plain CSS transition of the same length, so it never needs a frame loop and is hidden under reduced motion, where the digits still run. The block speaks twice: once when the clock starts ("Undo is available for 10 seconds") and once at zero. It never reads out every second. Closing the dialog stops the clock and opening it starts a new one.

Does a timed Undo fail people who need longer?

It would if it were the only way. That is why it isn't: the moment the message is sent, the header's Send turns into Return to drafts, which does the same thing with no clock, and the expired Undo says so in a line under the message. The window is a convenience for the quick case, not a limit on a person's ability to change their mind. In your app keep an equivalent always on the page, such as an Unsend or a Move to drafts action on the message, for as long as your server can honour it.

What do the buttons do here, and how do I wire them up?

Nothing is sent. Closing the dialog, with View message, Close or Escape, leaves the drawn message as Sent; View message also tints the row as opened. Undo, while it lasts, takes the message back to Draft and says so. In your app hold the message server-side for the length of the window or send a recall, and make undo call it; if the server can no longer honour it, say so inside the dialog instead of pretending.

Where does focus go, and how does it close?

The real dialog opens with focus on View message, the one primary, not on Undo, so a stray Enter doesn't take the message back. Escape and Close keep it sent. After any result the header's button has changed (Send to Return to drafts and back), so about 300 ms later focus moves to the button that took its place. View message is the focus target through initialFocus.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet with its pair pinned: View message above, Undo under it, both the full width. The real dialog keeps the Dialog's own phone layout, sticking to the bottom edge with the same stacked pair.

  • Pro

    Success Dialog

    Status and errors

    An invoice page with its payment confirmation shown open: a dialog with a drawn check, a receipt list with a copy button for the reference, and one button that settles the invoice.

  • Pro

    Unsaved Changes Dialog

    Confirm and consent

    An essay editor with its leave prompt shown open: an alert dialog that lists what changed since the last save and offers Save and leave, Discard changes and Keep editing.

  • Pro

    Session Timeout Dialog

    Status and errors

    A bank's accounts page with an alert dialog shown open that warns before an idle session ends: a one-minute clock with a draining bar, Stay signed in and Sign out now, and a signed-out view at zero.

  • Pro

    Rate Limit Dialog

    Status and errors

    An API console with its 429 notice shown open: a dialog with the quota used, the plan's limit, a clock to the reset and a Countdown Button that turns into Retry request when the wait ends.

  • Pro

    Confirm Action Dialog

    Confirm and consent

    A newsletter editor with its publish confirmation shown open: an alert dialog that lists what happens to the issue, a box that remembers the answer, and a foot that gives the question back.

  • Pro

    Save Draft Dialog

    Notes and editing

    A publishing editor with its save-draft dialog shown open: a name, a folder, an autosave choice with a sentence that follows it, and a discard that asks again in the same row.