---
title: Unsaved Changes Guard
slug: unsaved-changes-guard
kind: pattern
summary: The screen knows whether it holds work that is not saved, says so continuously, and intercepts anything that would throw it away.
problem: >-
  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.
family: [edit, recover]
data_shape: [record]
principles: [orientation, consistency]
interaction: [editing]
density: low
complexity: medium
status: stable
visibility: public
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.
alternatives:
  - slug: autosave
    when: The better fix is to stop having unsaved changes at all.
  - slug: confirmation-vs-undo
    when: The risk is an action being performed, not work in progress being lost.
ask_leo: |
  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:
  - title: Confirmation vs Undo
    url: /patterns/confirmation-vs-undo
    summary: The general rule for when a screen should interrupt someone at all.
---

## 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](/patterns/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.
