Documentation Index Fetch the complete documentation index at: https://mintlify.com/tanstack/router/llms.txt
Use this file to discover all available pages before exploring further.
TanStack Router treats search parameters (query strings) as first-class citizens, providing type-safe parsing, validation, and updates.
Defining Search Params
Use validateSearch to define and validate search parameters:
const postsRoute = createRoute ({
getParentRoute : () => rootRoute ,
path: '/posts' ,
validateSearch : ( search ) => ({
page: Number ( search . page ) || 1 ,
filter: ( search . filter as string ) || 'all' ,
sort: search . sort as 'date' | 'title' | undefined ,
}),
})
Using Search Params
Access search params in components with useSearch:
import { useSearch } from '@tanstack/react-router'
function PostsList () {
const { page , filter , sort } = useSearch ({ from: '/posts' })
return (
< div >
< h1 > Posts - Page { page } </ h1 >
< p > Filter: { filter } </ p >
{ sort && < p > Sorted by: { sort } </ p > }
</ div >
)
}
Strict Mode
Enable strict mode for route-specific types:
// Type-safe: Only search params from '/posts' route
const search = useSearch ({ from: '/posts' , strict: true })
// Loose: Union of all search params from all routes
const search = useSearch ({ strict: false })
Validation with Validators
Use validation libraries like Zod for schema validation:
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const searchSchema = z . object ({
page: z . number (). min ( 1 ). default ( 1 ),
filter: z . enum ([ 'all' , 'published' , 'draft' ]). default ( 'all' ),
tags: z . array ( z . string ()). optional (),
})
const postsRoute = createRoute ({
getParentRoute : () => rootRoute ,
path: '/posts' ,
validateSearch: zodValidator ( searchSchema ),
})
Supported validators:
Zod : @tanstack/zod-adapter
Valibot : @tanstack/valibot-adapter
ArkType : @tanstack/arktype-adapter
Updating Search Params
Update search params using Link or navigate:
Using Links
// Set search params
< Link to = "/posts" search = { { page: 2 , filter: 'published' } } >
Page 2
</ Link >
// Update existing search params
< Link to = "." search = { ( prev ) => ({ ... prev , page: prev . page + 1 }) } >
Next Page
</ Link >
// Clear specific params
< Link to = "." search = { ( prev ) => ({ ... prev , filter: undefined }) } >
Clear Filter
</ Link >
Using Navigate
import { useNavigate } from '@tanstack/react-router'
function FilterComponent () {
const navigate = useNavigate ()
const handleFilterChange = ( filter : string ) => {
navigate ({
to: '.' ,
search : ( prev ) => ({ ... prev , filter }),
})
}
return (
< select onChange = { ( e ) => handleFilterChange ( e . target . value ) } >
< option value = "all" > All </ option >
< option value = "published" > Published </ option >
< option value = "draft" > Draft </ option >
</ select >
)
}
Search Param Serialization
By default, TanStack Router uses JSON for complex values:
// URL: /posts?tags=["react","typescript"]
const search = { tags: [ 'react' , 'typescript' ] }
Custom Serialization
Provide custom serializers for search parameters:
import { parseSearchWith , stringifySearchWith } from '@tanstack/react-router'
const router = createRouter ({
routeTree ,
parseSearch: parseSearchWith (( str ) => JSON . parse ( str )),
stringifySearch: stringifySearchWith ( JSON . stringify , JSON . parse ),
})
The router uses qss for encoding/decoding search strings:
import { encode , decode } from '@tanstack/router-core'
// Encode object to search string
const searchStr = encode ({ page: 1 , filter: 'all' })
// Result: "page=1&filter=all"
// Decode search string to object
const search = decode ( 'page=1&filter=all' )
// Result: { page: '1', filter: 'all' }
Search Middleware
Transform search params before they’re used:
const postsRoute = createRoute ({
getParentRoute : () => rootRoute ,
path: '/posts' ,
validateSearch : ( search ) => ({
page: Number ( search . page ) || 1 ,
filter: search . filter as string ,
}),
search: {
middlewares: [
// Normalize filter values
({ search , next }) => {
const normalized = {
... search ,
filter: search . filter ?. toLowerCase (),
}
return next ( normalized )
},
// Add defaults
({ search , next }) => {
const withDefaults = {
sort: 'date' ,
... search ,
}
return next ( withDefaults )
},
],
},
})
Parent Search Params
Child routes inherit search params from parents:
const layoutRoute = createRoute ({
getParentRoute : () => rootRoute ,
id: 'layout' ,
validateSearch : ( search ) => ({
theme: search . theme as 'light' | 'dark' | undefined ,
}),
})
const postsRoute = createRoute ({
getParentRoute : () => layoutRoute ,
path: '/posts' ,
validateSearch : ( search ) => ({
// Inherits 'theme' from parent
... search ,
page: Number ( search . page ) || 1 ,
}),
})
Access in component:
function PostsList () {
const search = useSearch ({ from: '/posts' })
// Both 'theme' and 'page' are available
console . log ( search . theme , search . page )
}
Search Schema Types
Define TypeScript types for search params:
type PostsSearch = {
page : number
filter : 'all' | 'published' | 'draft'
tags ?: string []
sort ?: 'date' | 'title' | 'author'
}
const postsRoute = createRoute ({
getParentRoute : () => rootRoute ,
path: '/posts' ,
validateSearch : ( search ) : PostsSearch => ({
page: Number ( search . page ) || 1 ,
filter: ( search . filter as PostsSearch [ 'filter' ]) || 'all' ,
tags: search . tags as string [] | undefined ,
sort: search . sort as PostsSearch [ 'sort' ],
}),
})
Strict Search Mode
Enforce strict search parameter validation:
const router = createRouter ({
routeTree ,
search: {
strict: true , // Unknown search params cause errors
},
})
With strict mode:
Unknown search params are removed
Only params defined in validateSearch are allowed
Helps catch typos and invalid params
Preserving Search Params
Maintain search params across navigations:
// Keep all current search params
< Link to = "/posts" search = { ( prev ) => prev } >
Posts
</ Link >
// Keep specific params
< Link to = "/posts" search = { ( prev ) => ({ page: prev . page }) } >
Posts (preserve page)
</ Link >
// Merge with new params
< Link to = "/posts" search = { ( prev ) => ({ ... prev , filter: 'published' }) } >
Published Posts
</ Link >
Search Params in Loaders
Access search params in loader functions:
const postsRoute = createRoute ({
getParentRoute : () => rootRoute ,
path: '/posts' ,
validateSearch : ( search ) => ({
page: Number ( search . page ) || 1 ,
filter: search . filter as string ,
}),
loaderDeps : ({ search }) => ({
page: search . page ,
filter: search . filter ,
}),
loader : async ({ deps }) => {
const posts = await fetchPosts ({
page: deps . page ,
filter: deps . filter ,
})
return { posts }
},
})
The loaderDeps function makes search params part of the cache key, so data refetches when params change.
URL Building
Build URLs with search params:
import { useRouter } from '@tanstack/react-router'
function Component () {
const router = useRouter ()
const url = router . buildLocation ({
to: '/posts' ,
search: { page: 2 , filter: 'published' },
})
console . log ( url . href )
// "/posts?page=2&filter=published"
}
Common Patterns
function Pagination () {
const { page } = useSearch ({ from: '/posts' })
return (
< div >
< Link to = "." search = { ( prev ) => ({ ... prev , page: page - 1 }) } >
Previous
</ Link >
< span > Page { page } </ span >
< Link to = "." search = { ( prev ) => ({ ... prev , page: page + 1 }) } >
Next
</ Link >
</ div >
)
}
Filtering
function FilterBar () {
const navigate = useNavigate ()
const { filter } = useSearch ({ from: '/posts' })
const handleFilterChange = ( newFilter : string ) => {
navigate ({
to: '.' ,
search : ( prev ) => ({ ... prev , filter: newFilter , page: 1 }),
})
}
return (
< select value = { filter } onChange = { ( e ) => handleFilterChange ( e . target . value ) } >
< option value = "all" > All Posts </ option >
< option value = "published" > Published </ option >
< option value = "draft" > Drafts </ option >
</ select >
)
}
Sorting
function SortControls () {
const { sort } = useSearch ({ from: '/posts' })
return (
< div >
< Link to = "." search = { ( prev ) => ({ ... prev , sort: 'date' }) } >
Sort by Date
</ Link >
< Link to = "." search = { ( prev ) => ({ ... prev , sort: 'title' }) } >
Sort by Title
</ Link >
< Link to = "." search = { ( prev ) => ({ ... prev , sort: undefined }) } >
Default Sort
</ Link >
</ div >
)
}
Next Steps
Path Params Learn about path parameter handling
Loaders Use search params in data loading
Type Safety Configure end-to-end type safety
Navigation Navigate with search parameters