Unsaved Changes Guard | LlamaPress Pattern Catalog
LlamaPress
Edit & Manipulate
Pattern

Unsaved Changes Guard

The screen knows whether it holds work that is not saved, says so continuously, and intercepts anything that would throw it away.

Edit & Manipulate Feedback & Recovery

Someone fills in half a form, clicks a link, and the work is gone with no warning. Or the opposite: a screen warns about leaving when nothing was actually changed, so people learn to dismiss the warning and lose work anyway.

Live demo — try it Open full screen
Use when
  • A screen holds work in progress that is not yet persisted.
  • Losing that work would cost more than a few seconds to redo.
  • Navigation away is easy — a nav bar, a back link, a drawer that closes on Escape.
Avoid when
  • The form autosaves. Then the answer is a save-state indicator, not a guard.
  • Nothing is lost by leaving, such as a filter or a search box.
  • The work is one field. A guard costs more attention than the retyping.

Anatomy

┌──────────────────────────────────────────────┐
│ Invoice INV-1041          ● Unsaved changes  │  ← continuous, quiet
│ …fields…                    [ Save ]         │
└──────────────────────────────────────────────┘

  on navigate away:
  ┌────────────────────────────────────────┐
  │ Save your changes to INV-1041?         │
  │ 3 fields have been edited.             │
  │  [Cancel]  [Discard]  [Save and leave] │  ← three options, not two
  └────────────────────────────────────────┘

Three obligations:

  1. Real dirty tracking. Compare against the loaded values. A guard that fires because someone clicked into a field is the fastest way to teach people to ignore it.
  2. A continuous indicator. The dialog should be a backstop, not the announcement. If the only time you learn about unsaved work is when you try to leave, the screen has been keeping a secret.
  3. Three choices at the interception. Save-and-continue is the one most often missing, and it is the one people want most of the time.

Why it works

It converts an invisible risk into a visible state. Work-in-progress is real to the person and invisible to the software unless someone chooses to model it — and once it is modelled, the indicator, the guard and the beforeunload handler all fall out of the same flag.

Naming the count matters more than it looks. "You have unsaved changes" is a category; "3 unsaved changes to this invoice" is a fact, and people make different decisions about facts.

The better fix, where it is available

A guard is a mitigation for a design where work can be lost. Where the domain allows it, autosave removes the problem instead of managing it, and the screen then owes a save-state indicator rather than a dialog.

The honest test is how much a person would lose. Drafting a long note: autosave. A financial record where a half-entered state is meaningless or dangerous: explicit save, with a guard.

Getting it wrong

  • False positives. Firing on focus, on a value typed and undone, or on a field the system itself populated. Each one costs credibility, and credibility is the only thing making the dialog work.
  • Two buttons. Cancel and Discard, with no way to save, is a dialog that punishes the person for having done the right thing.
  • Only the browser warning. The generic beforeunload text cannot say what is at stake, and it does not fire on in-app navigation at all.
  • Warning after a successful save, which happens whenever the flag is set on input but never cleared on response.
  • Blocking a genuine escape. Someone must always be able to leave. The guard asks; it does not trap.

Exemplars

Google Docs is the strongest argument for the alternative: it removed the entire category by autosaving, and replaced the guard with a quiet "All changes saved" indicator — an idea now so normal that its absence feels like a defect.

GitHub's pull request comment box preserves drafts across navigation instead of guarding them, which is the third option: neither block nor lose, just keep.

Figma shows the save-state indicator done as a continuous, low-attention element rather than an interruption.

The extractable rule: a guard is what you build when you have decided work can be lost. Before building it, check whether that decision was necessary — the best version of this pattern is the one you did not need.

Instead of this, consider

Same problem, different trade-off. Pick on the condition, not on taste.

Autosave
When: The better fix is to stop having unsaved changes at all.
Confirmation vs Undo
When: The risk is an action being performed, not work in progress being lost.

Ask Leo for this

Paste this into your Leo chat. It describes the behaviour, not the code, so Leo can fit it to your data.

Prompt
Add an unsaved-changes guard to this form.

- Track whether the form is actually dirty by comparing current values to the
  values it loaded with. Do not treat focus, or typing then retyping the same
  value, as a change.
- Show the state continuously and quietly — an "Unsaved changes" marker near
  the save action — so leaving is never the first time someone learns about it.
- Intercept in-app navigation: links, the back link, closing a drawer or
  modal, and switching tabs within the record.
- Also register the browser's own beforeunload warning for closing the tab or
  hitting reload.
- The interception is a dialog with three choices, not two: save and continue,
  discard and continue, and cancel. A two-button dialog forces people to
  choose between losing work and being stuck.
- Say what will be lost — "3 unsaved changes to this invoice" — not "you have
  unsaved changes".
- Clear the state the moment a save succeeds, and never warn after a
  successful save.

Related

Pattern
Confirmation vs Undo
The general rule for when a screen should interrupt someone at all.