Skip to main content

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

parsers
ParserMap
required
An object mapping search param keys to their parser configurations.
const parsers = {
  q: parseAsString.withDefault(''),
  page: parseAsInteger.withDefault(1),
  category: parseAsString
}
options
object
Optional configuration object.

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>
loaderOptions
LoaderFunctionOptions
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]>
key
keyof Parsers
required
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=1Cache { q: 'foo', page: 1 }
Request 2: ?q=bar&page=2Cache { 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

Build docs developers (and LLMs) love