Announcing nuqs version 2
nuqs

Server-Side usage

Type-safe search params on the server

This feature is available for Next.js only.

If you wish to access the searchParams in a deeply nested Server Component (ie: not in the Page component), you can use createSearchParamsCache to do so in a type-safe manner.

Note

Parsers don’t validate your data. If you expect positive integers or JSON-encoded objects of a particular shape, you’ll need to feed the result of the parser to a schema validation library, like Zod.

Built-in validation support is coming. Read the RFC

searchParams.ts
import {
  createSearchParamsCache,
  parseAsInteger,
  parseAsString
} from 'nuqs/server'
// Note: import from 'nuqs/server' to avoid the "use client" directive
 
export const searchParamsCache = createSearchParamsCache({
  // List your search param keys and associated parsers here:
  q: parseAsString.withDefault(''),
  maxResults: parseAsInteger.withDefault(10)
})
page.tsx
import { searchParamsCache } from './searchParams'
import { type SearchParams } from 'nuqs/server'
 
type PageProps = {
  searchParams: Promise<SearchParams> // Next.js 15+: async searchParams prop
}
 
export default async function Page({ searchParams }: PageProps) {
  // ⚠️ Don't forget to call `parse` here.
  // You can access type-safe values from the returned object:
  const { q: query } = searchParamsCache.parse(await searchParams)
  return (
    <div>
      <h1>Search Results for {query}</h1>
      <Results />
    </div>
  )
}
 
function Results() {
  // Access type-safe search params in children server components:
  const maxResults = searchParamsCache.get('maxResults')
  return <span>Showing up to {maxResults} results</span>
}

The cache will only be valid for the current page render (see React’s cache function).

Note: the cache only works for server components, but you may share your parser declaration with useQueryStates for type-safety in client components:

searchParams.ts
import {
  parseAsFloat,
  createSearchParamsCache
} from 'nuqs/server'
 
export const coordinatesParsers = {
  lat: parseAsFloat.withDefault(45.18),
  lng: parseAsFloat.withDefault(5.72)
}
export const coordinatesCache = createSearchParamsCache(coordinatesParsers)
page.tsx
import { coordinatesCache } from './searchParams'
import { Server } from './server'
import { Client } from './client'
 
export default async function Page({ searchParams }) {
  coordinatesCache.parse(await searchParams)
  return (
    <>
      <Server />
      <Suspense>
        <Client />
      </Suspense>
    </>
  )
}
server.tsx
import { coordinatesCache } from './searchParams'
 
export function Server() {
  const { lat, lng } = coordinatesCache.all()
  // or access keys individually:
  const lat = coordinatesCache.get('lat')
  const lng = coordinatesCache.get('lng')
  return (
    <span>
      Latitude: {lat} - Longitude: {lng}
    </span>
  )
}
client.tsx
'use client'
 
import { useQueryStates } from 'nuqs'
import { coordinatesParsers } from './searchParams'
 
export function Client() {
  const [{ lat, lng }, setCoordinates] = useQueryStates(coordinatesParsers)
  // ...
}

On this page

No Headings