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.
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.
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.
import { useBlocker } from '@tanstack/react-router'
export function useLeaveBlocker(isDirty: boolean) {
return useBlocker({
shouldBlockFn: () => isDirty,
enableBeforeUnload: isDirty,
withResolver: true,
})
}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.