Navigation Blocking
Purpose and Scope
Navigation blocking is the Router feature for pausing a navigation before it changes the current screen. The guide frames the common cases as unsaved changes, an in-progress form, and payment flows, where leaving the current page could discard user work or interrupt a sensitive transaction. A blocked navigation should not silently fail; the application should ask the user to confirm, either with a browser dialog or with custom application UI. If the user confirms, the pending navigation can continue. If the user cancels, the pending navigation is stopped and the user remains where they are.
Sources: docs/router/guide/navigation-blocking.md
This page focuses on how to think about blockers in a Router application and how to apply the hook-based API shown in the repository docs. It also calls out the boundary between navigation control and real security. Blocking a transition is a user-experience safeguard, not a substitute for server-side authorization or validation. That distinction matters especially in TanStack Start applications, where Router handles route transitions while server functions and server routes must still authorize private data access independently.
Sources: docs/router/guide/navigation-blocking.md, docs/start/framework/react/guide/authentication-overview.md, docs/start/framework/react/guide/authentication-server-primitives.md
Relevant Source Files
- docs/router/guide/navigation-blocking.md - Primary Router guide for navigation blocking, including the definition of blockers, examples for dirty forms, the two usage styles, and the relationship between custom UI and browser unload behavior.
- docs/start/framework/react/build-from-scratch.md - Shows that TanStack Start applications are configured around TanStack Router, a route tree, and a root application file, which is the environment where Router navigation behavior is applied in full-stack apps.
- docs/start/framework/react/comparison.md - Positions TanStack Start as built on TanStack Router and separates routing capabilities from full-stack framework capabilities, useful when deciding whether blocking belongs in Router or Start-specific code.
- docs/start/framework/react/getting-started.md - Points new Start users toward Router-focused examples and the routing guide after project creation, which helps locate navigation blocking as a Router concept used inside Start projects.
- docs/start/framework/react/guide/authentication-overview.md - Defines route protection patterns and emphasizes the difference between route or UI control and data/API security boundaries.
- docs/start/framework/react/guide/authentication-server-primitives.md - Reinforces that server functions and server routes must authorize private data independently, which is an important caution when blockers are used around authenticated or transactional flows.
How Blocking Works
The Router documentation describes navigation blocking as adding one or more blocker layers to the underlying history API. When navigation is attempted and blockers are present, the transition is paused rather than immediately completed. For navigations controlled at the router level, the blocker logic can run asynchronously and sequentially, which lets the application show a confirmation modal, wait for user input, or perform another decision-making task. The important operational model is that a blocker participates in a chain: each blocker can allow the process to move on, while a cancellation stops the remaining blocker work and cancels the navigation.
Sources: docs/router/guide/navigation-blocking.md
There are two broad categories of navigation attempts. Router-controlled transitions are the ones the application can mediate directly, such as link clicks or programmatic navigation that pass through the Router. These can use custom UI because the application remains in control while the decision is pending. Browser-level unload events are different. Closing a tab, refreshing the page, or otherwise unloading assets is outside normal Router control, so the browser’s unload mechanism is used. In that case, users see the browser’s generic leave-page dialog rather than an application-specific modal.
Sources: docs/router/guide/navigation-blocking.md
The practical result is that applications should design for both graceful in-app confirmation and unavoidable browser behavior. A polished form route can use Router logic to show a tailored message when a user clicks a link to another route, but the same route may rely on the browser’s own dialog if the user refreshes or closes the tab. This is why blocking should be connected to clear application state, such as whether a form is dirty, whether a payment step is incomplete, or whether a long-running process still needs an explicit abandon action.
Sources: docs/router/guide/navigation-blocking.md
Hook-Based Blocking Flow
The documented hook-based path uses the framework Router package. In React, the guide imports the hook from the React Router package; in Solid, it imports the same concept from the Solid Router package. The example keeps a dirty-form flag in component state and registers a blocking decision function. If the form is not dirty, the function reports that no blocking is needed. If the form is dirty, the example asks the user whether they want to leave and converts that answer into the blocker decision. This keeps the guard close to the component state that actually knows whether work would be lost.
Sources: docs/router/guide/navigation-blocking.md
import { useBlocker } from '@tanstack/react-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
},
})
}The example is intentionally small, but it captures the most important implementation detail: the blocker should be derived from application truth, not from the attempted destination alone. A dirty flag, checkout status, or step completion value should be the source of the decision. When that value changes back to safe, the blocker should stop blocking. This avoids frustrating users after a successful save and prevents stale prompts from appearing when there is nothing left to protect. The docs show this as component-local state, but the same principle applies when the state comes from a form library or route context.
Sources: docs/router/guide/navigation-blocking.md
The hook also receives typed current and next location information. The guide shows a scenario that blocks only a specific transition: from one route to another full path with a particular path parameter and search value. That example demonstrates that the blocker decision can combine route identity, route path, typed path parameters, and typed search parameters. Instead of treating every attempted transition the same way, applications can make focused decisions, such as allowing navigation among substeps while blocking exits to unrelated sections until the user explicitly confirms.
Sources: docs/router/guide/navigation-blocking.md
Resolver-Based Decisions and Location-Aware Prompts
The location-aware example also enables resolver behavior and reads the returned control values. The returned object includes a way to proceed, a way to reset, and a status value. Conceptually, this pattern is useful when the application wants to show its own confirmation UI rather than relying on a synchronous browser confirmation call. The blocking predicate decides that a navigation should be paused, and the UI can then render a dialog, bottom sheet, or route-level warning. The user’s choice calls the appropriate continuation or reset operation.
Sources: docs/router/guide/navigation-blocking.md
const { proceed, reset, status } = useBlocker({
shouldBlockFn: ({ current, next }) => {
return (
current.routeId === '/foo' &&
next.fullPath === '/bar/$id' &&
next.params.id === 123 &&
next.search.hello === 'world'
)
},
withResolver: true,
})This model is especially helpful for design systems and product flows that need more than a simple yes-or-no browser prompt. For example, a route can display the name of the destination, explain which unsaved section will be lost, or offer a save-and-continue action before proceeding. Because the guide emphasizes sequential asynchronous blocker execution, custom UI can be treated as part of the navigation decision rather than as an afterthought. The application should still keep the decision fast and predictable, because a blocked navigation is a pending user action that should not be left unresolved.
Sources: docs/router/guide/navigation-blocking.md
Component-Based Blocking and Application Placement
The Router guide states that navigation blocking can be used in two ways: hook or logical blocking, and component-based blocking. The selected source evidence expands the hook path, so this page treats component-based blocking as the same feature exposed through a component-oriented style rather than documenting unsupported prop names. Use the hook style when the decision is naturally colocated with component logic or route state. Use a component-oriented style when the team prefers declarative tree placement, such as placing a blocker near the form or layout section whose unsaved state should be protected.
Sources: docs/router/guide/navigation-blocking.md
Placement matters because blockers apply to navigation behavior, not merely to a button click. A form-level blocker can protect against sidebar links, back-button navigation, and other route changes that pass through the Router. A layout-level blocker can protect a whole wizard or authenticated workspace subtree if that layout owns the state that determines whether leaving is dangerous. Conversely, an overly broad blocker can make ordinary navigation feel broken. The safest design is to place the blocker at the narrowest level that can accurately know when the user has something at risk.
Sources: docs/router/guide/navigation-blocking.md
TanStack Start and Security Boundaries
TanStack Start is built on TanStack Router, so Router navigation concepts are relevant inside Start applications. The Start build-from-scratch guide describes the basic app shape around Router configuration, a route tree, and the root application. The Start getting-started guide also directs users to Router examples and routing material after project creation. That means a Start route that contains a dirty form can use the same Router navigation-blocking model for client-side route transitions, while still benefiting from Start’s server rendering and full-stack capabilities elsewhere in the app.
Sources: docs/start/framework/react/build-from-scratch.md, docs/start/framework/react/getting-started.md, docs/start/framework/react/comparison.md
Do not confuse a blocker with authentication or authorization. The Start authentication overview recommends thinking about route protection, component-level protection, and data/API protection as separate layers. The server primitives guide is even more explicit: route guards are for user experience, while server functions, server routes, and endpoints that touch private data must authorize the request themselves. Navigation blocking follows the same principle. It can prevent accidental abandonment of a screen, but it cannot protect a mutation endpoint, enforce payment rules, or prove that a user is allowed to access private data.
Sources: docs/start/framework/react/guide/authentication-overview.md, docs/start/framework/react/guide/authentication-server-primitives.md
Practical Checklist
Before adding a blocker, define the condition that makes navigation unsafe. Common conditions include unsaved edits, partially completed forms, and payment state that has not been resolved. Then decide whether an immediate confirmation is enough or whether the application needs custom UI with an explicit proceed and reset flow. Finally, make sure the blocker state is cleared when the user saves, submits, cancels intentionally, or otherwise reaches a safe state. A blocker that remains active after the risk is gone will train users to ignore prompts and may make the Router appear unreliable.
Sources: docs/router/guide/navigation-blocking.md
When the destination matters, use the typed current and next location values shown in the guide. Route identity, full path, path parameters, and search parameters let the application block only the transitions that are actually risky. When the risk is independent of destination, a simple dirty-state predicate is easier to reason about. In either case, test both router-controlled navigation and browser unload scenarios. The former can show custom UI; the latter may fall back to the browser’s own confirmation behavior. For related topics, read the guides on navigation and links, authenticated routes, data loading, and Start authentication boundaries.
Sources: docs/router/guide/navigation-blocking.md, docs/start/framework/react/guide/authentication-overview.md