Browse documentation
Agent docs/router/navigation-blocker
recipe

Protect navigation away from a dirty form

Use useBlocker resolver state for accessible leave confirmation and register beforeunload only while dirty.

View raw Markdown
CONTRACT

Execution contract

Use when
Use when leaving the current route would discard explicitly detectable unsaved user input.
Avoid when
Do not block navigation for refetchable data or disposable UI state.
Preconditions
Define reliable isDirty state, reset behavior after save, and a browser-unload policy.
Verification
Cover Link, history, refresh, tab close, and post-save leave; pristine state must never prompt.
Failure mode
A prompt after successful save usually means the mutation success path did not reset dirty state.
Security
Navigation blocking protects experience, not persistence, draft recovery, or concurrent-write control.
01

Blocking semantics

A true result from shouldBlockFn means block. With withResolver: true, that result only enters blocked state; proceed or reset must then resolve the navigation.

02

Minimal resolver

Use the same dirty state for enableBeforeUnload to cover refresh and tab close. A custom dialog needs an accessible name, keyboard handling, and focus management; this sample shows only the Router state contract.

src/features/editor/useLeaveBlocker.tstsx
import { useBlocker } from '@tanstack/react-router'

export function useLeaveBlocker(isDirty: boolean) {
  return useBlocker({
    shouldBlockFn: () => isDirty,
    enableBeforeUnload: isDirty,
    withResolver: true,
  })
}
03

Verification checklist

Verify a clean form does not block; Link, back, refresh, and tab close behave correctly while dirty; proceed leaves and reset stays; successful save clears dirty state. Browsers use a system dialog for beforeunload, whose copy is not fully customizable.

PRIMARY SOURCEShttps://tanstack.com/router/latest/docs/guide/navigation-blocking
TanStack Atlas

Original bilingual knowledge · verified against primary sources

Friend linksGitHub