---
id: router.navigation-blocker
kind: recipe
product: router
framework: react
locale: en
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "^1"
verifiedAgainst: "@tanstack/react-router@1.170.18"
contentModel: 2
packages:
  - "@tanstack/react-router"
tasks:
  - "block-navigation"
  - "protect-dirty-form"
  - "confirm-route-leave"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/guide/navigation-blocking"
---

# Protect navigation away from a dirty form

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

## 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.

File: `src/features/editor/useLeaveBlocker.ts`

```tsx
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.

## Official sources

- https://tanstack.com/router/latest/docs/guide/navigation-blocking
