Agent Wranglers

Forms Recommended

Warn before leaving unsaved changes

When a form has unsaved changes, warn before the tab closes or the app moves to another screen. Never warn when nothing changed.

How to do it

Track whether the form differs from what was loaded. Add a beforeunload listener only while it does, and remove it after a save. Browsers show the dialog only after the person has interacted with the page. The listener can keep the page out of the back-forward cache. Block in-app navigation with the router's own guard, such as useBlocker in React Router or beforeRouteLeave in Vue Router.

Example

const warnOnLeave = (event: BeforeUnloadEvent) => event.preventDefault();

export function trackUnsaved(form: HTMLFormElement) {
  const loaded = JSON.stringify([...new FormData(form)]);
  form.addEventListener('input', () => {
    const isDirty = JSON.stringify([...new FormData(form)]) !== loaded;
    if (isDirty) addEventListener('beforeunload', warnOnLeave);
    else removeEventListener('beforeunload', warnOnLeave);
  });
}
def test_warns_with_unsaved_changes(page):
    page.goto("https://staging.example.com/invoices/new")
    page.get_by_label("Customer email").fill("ada@example.com")
    with page.expect_event("dialog") as dialog_info:
        page.close(run_before_unload=True)
    assert dialog_info.value.type == "beforeunload"
    dialog_info.value.dismiss()

How to check

A browser test edits a field and closes the page with unload handlers on. A beforeunload dialog appears. Without an edit, no dialog appears. After an edit, clicking an in-app link shows the router's warning and keeps the screen.

For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/forms/unsaved-changes-warning.