Overview
The createSearchParamsCache function creates a cache interface for accessing search parameters in deeply nested server components. It uses React’s cache function to ensure parsed values are available throughout the server component tree for a single request.
The cache only works in server components . For client components, use useQueryState or useQueryStates hooks.
Function Signature
function createSearchParamsCache < Parsers extends ParserMap >(
parsers : Parsers ,
options ?: { urlKeys ?: UrlKeys < Parsers > }
) : CacheInterface < Parsers >
Parameters
An object mapping search param keys to their parser configurations. const parsers = {
q: parseAsString . withDefault ( '' ),
page: parseAsInteger . withDefault ( 1 ),
category: parseAsString
}
Optional configuration object. Map internal state keys to different URL query parameter names. type UrlKeys < Parsers > = Partial < Record < keyof Parsers , string >>
Example: const cache = createSearchParamsCache (
{ query: parseAsString },
{ urlKeys: { query: 'q' } }
)
Return Value
Returns a CacheInterface with the following methods:
type CacheInterface < Parsers extends ParserMap > = {
parse : ParseFunction < Parsers >
all : () => inferParserType < Parsers >
get : < Key extends keyof Parsers >( key : Key ) => inferParserType < Parsers [ Key ]>
}
parse()
Parses the incoming search params and stores them in the cache for the current request.
type ParseFunction < Parsers extends ParserMap > = {
// Synchronous overload
(
searchParams : SearchParams ,
loaderOptions ?: LoaderFunctionOptions
) : inferParserType < Parsers >
// Asynchronous overload (Next.js 15+)
(
searchParams : Promise < SearchParams >,
loaderOptions ?: LoaderFunctionOptions
) : Promise < inferParserType < Parsers >>
}
searchParams
SearchParams | Promise<SearchParams>
required
The searchParams prop from your page component. In Next.js 15+, this may be a Promise. type SearchParams = Record < string , string | string [] | undefined >
When true, the loader will throw an error if a search params value is invalid for the given parser, rather than falling back to the parser’s default value (or null if no default is set). type LoaderFunctionOptions = {
strict ?: boolean
}
You must call parse() in your page component before accessing values with get() or all() in nested components.
get()
Retrieves a single cached search param value by key.
get < Key extends keyof Parsers >( key : Key ) : inferParserType < Parsers [ Key ]>
The key of the search param to retrieve. const page = searchParamsCache . get ( 'page' )
const query = searchParamsCache . get ( 'q' )
all()
Retrieves all cached search param values as an object.
all (): inferParserType < Parsers >
Returns an object containing all parsed search param values:
const { q , page , category } = searchParamsCache . all ()
Type Definitions
ParserMap
type ParserMap = Record < string , ParserWithOptionalDefault < any >>
type ParserWithOptionalDefault < T > = GenericParserBuilder < T > & {
defaultValue ?: T
}
inferParserType
TypeScript automatically infers the return type based on your parsers:
const searchParamsCache = createSearchParamsCache ({
count: parseAsInteger , // number | null
active: parseAsBoolean . withDefault ( false ), // boolean
tags: parseAsArrayOf ( parseAsString ) // string[] | null
})
const count = searchParamsCache . get ( 'count' ) // number | null
const active = searchParamsCache . get ( 'active' ) // boolean
const tags = searchParamsCache . get ( 'tags' ) // string[] | null
const all = searchParamsCache . all ()
// {
// count: number | null,
// active: boolean,
// tags: string[] | null
// }
Examples
Basic Usage
// searchParams.ts
import { createSearchParamsCache , parseAsString , parseAsInteger } from 'nuqs/server'
export const searchParamsCache = createSearchParamsCache ({
q: parseAsString . withDefault ( '' ),
page: parseAsInteger . withDefault ( 1 ),
category: parseAsString
})
// page.tsx
import { searchParamsCache } from './searchParams'
export default async function Page ({ searchParams }) {
const { q } = await searchParamsCache . parse ( searchParams )
return (
< div >
< h1 > Search: { q } </ h1 >
< Results /> { /* Can access cached params */ }
</ div >
)
}
// Results.tsx (nested server component)
import { searchParamsCache } from './searchParams'
export function Results () {
const page = searchParamsCache . get ( 'page' )
const category = searchParamsCache . get ( 'category' )
return < div > Page { page } - Category: { category || 'All' } </ div >
}
Next.js 15 Async searchParams
// Next.js 14 and earlier (synchronous)
export default function Page ({ searchParams }) {
const { q } = searchParamsCache . parse ( searchParams )
return < div > Search: { q } </ div >
}
// Next.js 15+ (asynchronous)
export default async function Page ({ searchParams }) {
const { q } = await searchParamsCache . parse ( searchParams )
return < div > Search: { q } </ div >
}
Strict Mode
export default async function Page ({ searchParams }) {
try {
// Throws if any parser fails
const params = await searchParamsCache . parse ( searchParams , { strict: true })
} catch ( error ) {
console . error ( 'Invalid search params:' , error )
notFound ()
}
}
URL Keys Mapping
const searchParamsCache = createSearchParamsCache (
{
searchQuery: parseAsString ,
pageNumber: parseAsInteger . withDefault ( 1 )
},
{
urlKeys: {
searchQuery: 'q' ,
pageNumber: 'page'
}
}
)
// URL: ?q=laptop&page=2
// Access via internal names:
const searchQuery = searchParamsCache . get ( 'searchQuery' ) // "laptop"
const pageNumber = searchParamsCache . get ( 'pageNumber' ) // 2
Using all()
import { searchParamsCache } from './searchParams'
export async function SearchResults () {
// Access all values at once
const { q , tags , sortBy } = searchParamsCache . all ()
const results = await searchDatabase ( q , tags , sortBy )
return (
< ul >
{ results . map ( result => (
< li key = { result . id } > { result . title } </ li >
)) }
</ ul >
)
}
Cache Lifecycle
The cache is scoped to a single page render using React’s cache function:
Created when the page component renders
Shared across all server components in the tree for that request
Cleared after the request completes
Isolated between different requests (no cross-request pollution)
// Each request gets its own cache instance
Request 1 : ? q = foo & page = 1 → Cache { q: 'foo' , page: 1 }
Request 2 : ? q = bar & page = 2 → Cache { q: 'bar' , page: 2 }
Error Handling
The cache will throw an error if you try to access values before calling parse():
// ❌ Will throw error
export default function Page ({ searchParams }) {
// parse() not called yet!
const page = searchParamsCache . get ( 'page' )
// Error: [nuqs] Cannot access search params before calling parse()
}
// ✅ Correct usage
export default async function Page ({ searchParams }) {
await searchParamsCache . parse ( searchParams )
const page = searchParamsCache . get ( 'page' ) // Works!
}
The cache will also throw if you call parse() multiple times with different inputs in the same request:
export default async function Page ({ searchParams }) {
await searchParamsCache . parse ( searchParams )
// ❌ Will throw error if searchParams changed
await searchParamsCache . parse ( differentSearchParams )
// Error: Cannot call parse() with different inputs in the same request
}
If parse() is called multiple times with the same input (e.g., in both generateMetadata and the page component), the cached result is safely returned.
Cache Guide Learn how to use the cache in server components
createLoader One-off parsing with createLoader
Parsers Learn about available parser types