File: navigation-blocking.md | Updated: 11/15/2025
Search...
+ K
Auto
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Maintainers Partners Support Learn StatsBETA Discord Merch Blog GitHub Ethos Brand Guide
Documentation
Framework
Solid
Version
Latest
Search...
+ K
Menu
Getting Started
Installation Guides
Routing
Guides
API
ESLint
Router Examples
Framework
Solid
Version
Latest
Menu
Getting Started
Installation Guides
Routing
Guides
API
ESLint
Router Examples
On this page
Copy Markdown
Navigation blocking is a way to prevent navigation from happening. This is typical if a user attempts to navigate while they:
In these situations, a prompt or custom UI should be shown to the user to confirm they want to navigate away.
How does navigation blocking work?
----------------------------------
Navigation blocking adds one or more layers of "blockers" to the entire underlying history API. If any blockers are present, navigation will be paused via one of the following ways:
How do I use navigation blocking?
---------------------------------
There are 2 ways to use navigation blocking:
Hook/logical-based blocking
---------------------------
Let's imagine we want to prevent navigation if a form is dirty. We can do this by using the useBlocker hook:
tsx
import { useBlocker } from '@tanstack/solid-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
},
})
// ...
}
import { useBlocker } from '@tanstack/solid-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
},
})
// ...
}
shouldBlockFn gives you type safe access to the current and next location:
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
// always block going from /foo to /bar/123?hello=world
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,
})
// ...
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
// always block going from /foo to /bar/123?hello=world
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,
})
// ...
}
Note that even if shouldBlockFn returns false, the browser's beforeunload event may still be triggered on page reloads or tab closing. To gain control over this, you can use the enableBeforeUnload option to conditionally register the beforeunload handler:
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty()) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
},
})
// ...
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty()) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
},
})
// ...
}
tsx
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
return (
<Block
shouldBlockFn={() => {
if (!formIsDirty) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
}}
enableBeforeUnload={formIsDirty}
/>
)
// OR
return (
<Block
shouldBlockFn={() => formIsDirty}
enableBeforeUnload={formIsDirty}
withResolver
>
{({ status, proceed, reset }) => <>{/* ... */}</>}
</Block>
)
}
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
return (
<Block
shouldBlockFn={() => {
if (!formIsDirty) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
}}
enableBeforeUnload={formIsDirty}
/>
)
// OR
return (
<Block
shouldBlockFn={() => formIsDirty}
enableBeforeUnload={formIsDirty}
withResolver
>
{({ status, proceed, reset }) => <>{/* ... */}</>}
</Block>
)
}
tsx
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
return (
<Block
shouldBlockFn={() => {
if (!formIsDirty()) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
}}
/>
)
// OR
return (
<Block shouldBlockFn={() => !formIsDirty} withResolver>
{({ status, proceed, reset }) => <>{/* ... */}</>}
</Block>
)
}
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
return (
<Block
shouldBlockFn={() => {
if (!formIsDirty()) return false
const shouldLeave = confirm('Are you sure you want to leave?')
return !shouldLeave
}}
/>
)
// OR
return (
<Block shouldBlockFn={() => !formIsDirty} withResolver>
{({ status, proceed, reset }) => <>{/* ... */}</>}
</Block>
)
}
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
const { proceed, reset, status } = useBlocker({
shouldBlockFn: () => formIsDirty,
withResolver: true,
})
// ...
return (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
const { proceed, reset, status } = useBlocker({
shouldBlockFn: () => formIsDirty,
withResolver: true,
})
// ...
return (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
}
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
const { proceed, reset, status } = useBlocker({
shouldBlockFn: () => formIsDirty(),
withResolver: true,
})
// ...
return (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
const { proceed, reset, status } = useBlocker({
shouldBlockFn: () => formIsDirty(),
withResolver: true,
})
// ...
return (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
}
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty) {
return false
}
const shouldBlock = new Promise<boolean>((resolve) => {
// Using a modal manager of your choice
modals.open({
title: 'Are you sure you want to leave?',
children: (
<SaveBlocker
confirm={() => {
modals.closeAll()
resolve(false)
}}
reject={() => {
modals.closeAll()
resolve(true)
}}
/>
),
onClose: () => resolve(true),
})
})
return shouldBlock
},
})
// ...
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty) {
return false
}
const shouldBlock = new Promise<boolean>((resolve) => {
// Using a modal manager of your choice
modals.open({
title: 'Are you sure you want to leave?',
children: (
<SaveBlocker
confirm={() => {
modals.closeAll()
resolve(false)
}}
reject={() => {
modals.closeAll()
resolve(true)
}}
/>
),
onClose: () => resolve(true),
})
})
return shouldBlock
},
})
// ...
}
tsx
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty()) {
return false
}
const shouldBlock = new Promise<boolean>((resolve) => {
// Using a modal manager of your choice
modals.open({
title: 'Are you sure you want to leave?',
children: (
<SaveBlocker
confirm={() => {
modals.closeAll()
resolve(false)
}}
reject={() => {
modals.closeAll()
resolve(true)
}}
/>
),
onClose: () => resolve(true),
})
})
return shouldBlock
},
})
// ...
}
import { useBlocker } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = createSignal(false)
useBlocker({
shouldBlockFn: () => {
if (!formIsDirty()) {
return false
}
const shouldBlock = new Promise<boolean>((resolve) => {
// Using a modal manager of your choice
modals.open({
title: 'Are you sure you want to leave?',
children: (
<SaveBlocker
confirm={() => {
modals.closeAll()
resolve(false)
}}
reject={() => {
modals.closeAll()
resolve(true)
}}
/>
),
onClose: () => resolve(true),
})
})
return shouldBlock
},
})
// ...
}
tsx
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
return (
<Block shouldBlockFn={() => formIsDirty} withResolver>
{({ status, proceed, reset }) => (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
)}
</Block>
)
}
import { Block } from '@tanstack/solid-router'
function MyComponent() {
const [formIsDirty, setFormIsDirty] = useState(false)
return (
<Block shouldBlockFn={() => formIsDirty} withResolver>
{({ status, proceed, reset }) => (
<>
{/* ... */}
{status === 'blocked' && (
<div>
<p>Are you sure you want to leave?</p>
<button onClick={proceed}>Yes</button>
<button onClick={reset}>No</button>
</div>
)}
</>
)}
</Block>
)
}
Custom Search Param Serialization
