nuqs

Utilities

Utilities for working with query strings

Serializer helper

To populate <Link> components with state values, you can use the createSerializer helper.

Pass it an object describing your search params, and it will give you a function to call with values, that generates a query string serialized as the hooks would do.

Example:

import {
  createSerializer,
  parseAsInteger,
  parseAsIsoDateTime,
  parseAsString,
  parseAsStringLiteral
} from 'nuqs/server' // can also be imported from 'nuqs' in client code
 
const searchParams = {
  search: parseAsString,
  limit: parseAsInteger,
  from: parseAsIsoDateTime,
  to: parseAsIsoDateTime,
  sortBy: parseAsStringLiteral(['asc', 'desc'] as const)
}
 
// Create a serializer function by passing the description of the search params to accept
const serialize = createSerializer(searchParams)
 
// Then later, pass it some values (a subset) and render them to a query string
serialize({
  search: 'foo bar',
  limit: 10,
  from: new Date('2024-01-01'),
  // here, we omit `to`, which won't be added
  sortBy: null // null values are also not rendered
})
// ?search=foo+bar&limit=10&from=2024-01-01T00:00:00.000Z

Base parameter

The returned serialize function can take a base parameter over which to append/amend the search params:

serialize('/path?baz=qux', { foo: 'bar' }) // /path?baz=qux&foo=bar
 
const search = new URLSearchParams('?baz=qux')
serialize(search, { foo: 'bar' }) // ?baz=qux&foo=bar
 
const url = new URL('https://example.com/path?baz=qux')
serialize(url, { foo: 'bar' }) // https://example.com/path?baz=qux&foo=bar
 
// Passing null removes existing values
serialize('?remove=me', { foo: 'bar', remove: null }) // ?foo=bar

Shorter search params keys

Just like useQueryStates, you can specify a urlKeys object to map the variable names defined by the parsers to shorter keys in the URL:

const serialize = createSerializer(
  {
    // 1. Use variable names that make sense for your domain/business logic
    latitude: parseAsFloat,
    longitude: parseAsFloat,
    zoomLevel: parseAsInteger
  },
  {
    // 2. Remap them to shorter keys in the URL
    urlKeys: {
      latitude: 'lat',
      longitude: 'lng',
      zoomLevel: 'z'
    }
  }
)
 
// 3. Use your variable names when calling the serializer,
// and the shorter keys will be rendered in the URL:
serialize({
  latitude: 45.18,
  longitude: 5.72,
  zoomLevel: 12
})
// ?lat=45.18&lng=5.72&z=12

Parser type inference

To access the underlying type returned by a parser, you can use the inferParserType type helper:

import { parseAsInteger, type inferParserType } from 'nuqs' // or 'nuqs/server'
 
const intNullable = parseAsInteger
const intNonNull = parseAsInteger.withDefault(0)
 
inferParserType<typeof intNullable> // number | null
inferParserType<typeof intNonNull> // number

For an object describing parsers (that you’d pass to createSearchParamsCache or to useQueryStates), inferParserType will return the type of the object with the parsers replaced by their inferred types:

import {
  parseAsBoolean,
  parseAsInteger,
  type inferParserType
} from 'nuqs' // or 'nuqs/server'
 
const parsers = {
  a: parseAsInteger,
  b: parseAsBoolean.withDefault(false)
}
 
inferParserType<typeof parsers>
// { a: number | null, b: boolean }

Standard Schema

Search param definitions can be turned into a Standard Schema for validating external sources and passing on type inference to other tools.

import {
  createStandardSchemaV1,
  parseAsInteger,
  parseAsString,
} from 'nuqs' // or 'nuqs/server'
 
// 1. Define your search params as usual
export const searchParams = {
  searchTerm: parseAsString.withDefault(''),
  maxResults: parseAsInteger.withDefault(10)
}
 
// 2. Then create a Standard Schema compatible validator
export const validateSearchParams = createStandardSchemaV1(searchParams)
 
// 3. Use it with other tools, like tRPC:
router({
  search: publicProcedure.input(validateSearchParams).query(...)
})

TanStack Router & validateSearch

Note

TanStack Router support is still experimental, see PR #953 for progress and feedback 🙏

You can pass the standard schema validator to TanStack Router’s validateSearch for type-safe linking to nuqs URL state, but in order to keep those values optional (as nuqs uses different defaults strategies than TSR), you need to mark the output as Partial, using the partialOutput option:

src/routes/search.tsx
import { createStandardSchemaV1 } from 'nuqs'
 
const validateSearch = createStandardSchemaV1(searchParams, {
  partialOutput: true
})
 
export const Route = createFileRoute('/search')({
  validateSearch
})

On this page