shadcn.io is not affiliated with official shadcn/ui
Unsaved Changes Dialog Block for React
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.
Installation
Overview
Stop a stray click from costing someone an hour of writing, with this unsaved-changes prompt for React with Base UI. It opens from the editor of Proseby, a long-form writing tool: the header holds an outline Back to posts Button, and the essay is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, its cover slot empty because the cover was removed. The Alert Dialog asks "Leave without saving?", counts the changes since the last save at 10:42 and lists each one as a glyph and words (a title edited, two paragraphs added, a cover removed), so shape and words carry the meaning, never colour. Three actions share one footer because the slug is three-way: Save and leave, the one Shine Button, Discard changes, outline, and Keep editing, ghost, which has focus. Leaving puts the drawn stage on the posts list, with the new title after a save and the old one after a discard, and the header's button becomes Open the essay, which brings the unsaved draft back. For preview the alert is drawn open in place when the page loads; Back to posts opens the real Alert Dialog, with the same list. For editors, forms and any screen with work in progress.
FAQ
Why is the alert already open, and what do I keep?
So you can see it without a click. On load the alert is a copy drawn in place over the editor: a native dialog element with the alertdialog role that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. Back to posts in the header opens the real Alert Dialog, modal, with its focus trap, Escape and focus on Keep editing, and the copy stays closed until a reload. Both show the same LeaveFields component. In your app keep the Alert Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Where does the list of changes come from?
changesOf(before, after) compares two typed essays, the saved one and the one on the page, and returns a row for each difference: a title that changed, paragraphs added or removed, a cover added or removed. The count in the heading is the length of that list, and "since your last save at 10:42" is a typed time, never the clock. In your app compare your editor's saved snapshot with its current state the same way, and keep the list short: a summary of ten rows is a wall.
Why are there three actions, and which one has focus?
Leaving can mean three things: keep the work and go, throw it away and go, or not go. Save and leave is the Shine Button and the only primary; Discard changes is outline; Keep editing is ghost and takes focus when the real alert opens, so a stray Enter backs out instead of losing work. Escape and a click on Keep editing do the same thing, and a click outside does not close the alert, since it has to be answered. Keep editing is the focus target through initialFocus.
What do Save and leave and Discard do here, and how do I wire them up?
They change local state and say so in a polite status message: nothing is stored or removed. Saving shows the posts list with the new title and "Saved in this demo"; discarding shows it with the old title and "Changes discarded". In your app save in save, keep the alert open with a busy state until the server answers, and leave only on success; on failure say so inside the alert and keep Keep editing in reach. Wire the same alert to your router's leave guard and to the browser's own prompt for a closed tab.
Where does focus go after a choice?
Keep editing returns to Back to posts, the only live control in the drawn editor. After Save and leave or Discard changes that button is gone, so about 300 ms later, once the alert has handed focus back, focus moves to Open the essay, the button that took its place. The copy shown on load moves no focus; Escape inside it and its buttons behave the same way.
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 actions pinned: Save and leave across the full width, and Keep editing and Discard changes side by side under it, while the list scrolls above. The real alert keeps the Alert Dialog's own layout: a centred card as wide as the screen allows.
Related
- Pro
Save Draft Dialog
Notes and editingA 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.
- Pro
Warning Dialog
Destructive confirmsA hosted database's Migrations page with its caution shown open: an alert dialog with a drawn freeze track and a checkbox that must be ticked before the destructive Run anyway turns on.
- Pro
Confirm Delete Dialog
Destructive confirmsA planner's project list with its delete confirmation shown open: an alert dialog that lists what goes with the project, asks for its name to be typed exactly, and keeps Delete off until it matches.
- Pro
Session Timeout Dialog
Status and errorsA 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
Confirm Action Dialog
Confirm and consentA 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
Downgrade Confirmation Billing
Upgrade and downgradeA downgrade that shows what it costs first: the limits that shrink, the folders now over the smaller plan's storage with a checkbox to archive each and a live meter, the day it takes effect, and an alert dialog that asks for a typed word.