CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nuqs

Type-safe search params state manager for Next.js - Like React.useState, but stored in the URL query string

Overall
score

96%

Overview
Eval results
Files

parsers.mddocs/

Type-Safe Parsers

Comprehensive parser system for converting between URL query string values and typed JavaScript/TypeScript values, with built-in support for common data types and utilities for creating custom parsers.

Capabilities

Built-in Parsers

Ready-to-use parsers for common data types with fluent configuration API.

/** String parser (default behavior) */
const parseAsString: ParserBuilder<string>;

/** Integer parser with NaN handling */
const parseAsInteger: ParserBuilder<number>;

/** Hexadecimal integer parser */
const parseAsHex: ParserBuilder<number>;

/** Float parser with NaN handling */
const parseAsFloat: ParserBuilder<number>;

/** Boolean parser (parses 'true'/'false' strings) */
const parseAsBoolean: ParserBuilder<boolean>;

/** Date parser from milliseconds since epoch */
const parseAsTimestamp: ParserBuilder<Date>;

/** Date parser from ISO-8601 strings */
const parseAsIsoDateTime: ParserBuilder<Date>;

Usage Examples:

import { 
  parseAsString, 
  parseAsInteger, 
  parseAsBoolean, 
  parseAsTimestamp 
} from "nuqs";

// Basic usage
const [name, setName] = useQueryState('name', parseAsString);
const [count, setCount] = useQueryState('count', parseAsInteger);
const [enabled, setEnabled] = useQueryState('enabled', parseAsBoolean);

// With default values
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
const [active, setActive] = useQueryState('active', parseAsBoolean.withDefault(false));

// Date handling
const [createdAt, setCreatedAt] = useQueryState('created', parseAsTimestamp);
const [updatedAt, setUpdatedAt] = useQueryState('updated', parseAsIsoDateTime);

Enum and Literal Parsers

Type-safe parsers for restricted sets of values.

/**
 * String-based enums provide better type-safety for known sets of values
 * @param validValues - The enum values you want to accept
 */
function parseAsStringEnum<Enum extends string>(validValues: Enum[]): ParserBuilder<Enum>;

/**
 * String-based literals provide better type-safety for known sets of values
 * @param validValues - The readonly list of allowed values
 */
function parseAsStringLiteral<Literal extends string>(
  validValues: readonly Literal[]
): ParserBuilder<Literal>;

/**
 * Number-based literals provide better type-safety for known sets of values
 * @param validValues - The readonly list of allowed values
 */
function parseAsNumberLiteral<Literal extends number>(
  validValues: readonly Literal[]
): ParserBuilder<Literal>;

Usage Examples:

// Enum parser
enum Status {
  DRAFT = 'draft',
  PUBLISHED = 'published',
  ARCHIVED = 'archived'
}

const [status, setStatus] = useQueryState(
  'status',
  parseAsStringEnum(Object.values(Status)).withDefault(Status.DRAFT)
);

// String literal parser
const themes = ['light', 'dark', 'auto'] as const;
const [theme, setTheme] = useQueryState(
  'theme',
  parseAsStringLiteral(themes).withDefault('auto')
);

// Number literal parser
const ratings = [1, 2, 3, 4, 5] as const;
const [rating, setRating] = useQueryState(
  'rating',
  parseAsNumberLiteral(ratings)
);

Array Parser

Parser for comma-separated arrays with configurable item parsers.

/**
 * A comma-separated list of items
 * @param itemParser - Parser for each individual item in the array
 * @param separator - The character to use to separate items (default ',')
 */
function parseAsArrayOf<ItemType>(
  itemParser: Parser<ItemType>,
  separator?: string
): ParserBuilder<ItemType[]>;

Usage Examples:

import { parseAsArrayOf, parseAsInteger, parseAsString } from "nuqs";

// Array of strings
const [tags, setTags] = useQueryState(
  'tags',
  parseAsArrayOf(parseAsString)
);

// Array of integers
const [ids, setIds] = useQueryState(
  'ids',
  parseAsArrayOf(parseAsInteger)
);

// Custom separator
const [categories, setCategories] = useQueryState(
  'categories',
  parseAsArrayOf(parseAsString, '|')
);

// Usage
setTags(['react', 'nextjs', 'typescript']);
setIds([1, 2, 3, 4]);
// URLs: ?tags=react,nextjs,typescript&ids=1,2,3,4

JSON Parser

Parser for complex objects serialized as JSON in the query string.

/**
 * Encode any object shape into the querystring value as JSON
 * Value is URI-encoded for safety, so it may not look nice in the URL
 * @param parser - Optional parser (eg: Zod schema) to validate after JSON.parse
 */
function parseAsJson<T>(parser?: (value: unknown) => T): ParserBuilder<T>;

Usage Examples:

import { parseAsJson } from "nuqs";

// Simple object
interface UserPrefs {
  notifications: boolean;
  theme: string;
  language: string;
}

const [prefs, setPrefs] = useQueryState(
  'prefs',
  parseAsJson<UserPrefs>().withDefault({
    notifications: true,
    theme: 'light',
    language: 'en'
  })
);

// With validation using Zod
import { z } from 'zod';

const prefsSchema = z.object({
  notifications: z.boolean(),
  theme: z.enum(['light', 'dark']),
  language: z.string().min(2)
});

const [validatedPrefs, setValidatedPrefs] = useQueryState(
  'prefs',
  parseAsJson(prefsSchema.parse)
);

Custom Parser Creation

Factory function for creating custom parsers with full type safety.

/**
 * Wrap a set of parse/serialize functions into a builder pattern parser
 * @param parser - Parser implementation with required parse and serialize functions
 */
function createParser<T>(
  parser: Required<Pick<Parser<T>, 'parse' | 'serialize'>> & Partial<Pick<Parser<T>, 'eq'>>
): ParserBuilder<T>;

interface Parser<T> {
  /** 
   * Convert a query string value into a state value.
   * If the string value does not represent a valid state value,
   * the parser should return null. Throwing an error is also supported.
   */
  parse: (value: string) => T | null;
  /** Render the state value into a query string value */
  serialize?: (value: T) => string;
  /** 
   * Check if two state values are equal.
   * This is used when using the clearOnDefault value, to compare the default
   * value with the set value. Useful for objects or arrays where referential
   * equality check will not work.
   */
  eq?: (a: T, b: T) => boolean;
}

Usage Examples:

import { createParser } from "nuqs";

// Custom date parser for YYYY-MM-DD format
const parseAsDateString = createParser({
  parse: (value: string) => {
    const date = new Date(value);
    return isNaN(date.getTime()) ? null : date;
  },
  serialize: (date: Date) => date.toISOString().split('T')[0]
});

// Custom object parser with specific serialization
interface Point {
  x: number;
  y: number;
}

const parseAsPoint = createParser({
  parse: (value: string) => {
    const [x, y] = value.split(',').map(Number);
    return isNaN(x) || isNaN(y) ? null : { x, y };
  },
  serialize: (point: Point) => `${point.x},${point.y}`,
  eq: (a: Point, b: Point) => a.x === b.x && a.y === b.y
});

const [point, setPoint] = useQueryState('point', parseAsPoint.withDefault({ x: 0, y: 0 }));

Parser Builder API

All parsers implement the builder pattern for configuration and default values.

interface ParserBuilder<T> extends Required<Parser<T>>, Options {
  /**
   * Specifying a default value makes the hook state non-nullable
   * @param defaultValue - The default value to use when query is missing
   */
  withDefault(
    defaultValue: NonNullable<T>
  ): Omit<ParserBuilder<T>, 'parseServerSide'> & {
    readonly defaultValue: NonNullable<T>;
    parseServerSide(value: string | string[] | undefined): NonNullable<T>;
  };

  /**
   * Set history type, shallow routing and scroll restoration options
   * @param options - Configuration options for the parser
   */
  withOptions<This, Shallow>(this: This, options: Options<Shallow>): This;

  /**
   * Use the parser in Server Components
   * Note: when multiple queries are presented (eg: /?a=1&a=2), only the first will be parsed
   * @param value - Query parameter value from page props
   */
  parseServerSide(value: string | string[] | undefined): T | null;
}

interface Options<Shallow = unknown> {
  /** How the query update affects page history (defaults to 'replace') */
  history?: 'replace' | 'push';
  /** Scroll to top after a query state update (defaults to false) */
  scroll?: boolean;
  /** Shallow mode keeps updates client-side only (defaults to true) */
  shallow?: Extract<Shallow | boolean, boolean>;
  /** Maximum time (ms) between URL query string updates (defaults to 50ms) */
  throttleMs?: number;
  /** React transition function for observing Server Component loading states */
  startTransition?: TransitionStartFunction;
  /** Clear key-value pair from URL when setting state to default value */
  clearOnDefault?: boolean;
}

type TransitionStartFunction = (callback: () => void) => void;

Configuration Examples:

// Parser with default value
const pageParser = parseAsInteger.withDefault(1);

// Parser with options
const searchParser = parseAsString.withOptions({
  history: 'push',
  throttleMs: 300
});

// Chaining configuration
const statusParser = parseAsStringEnum(['active', 'inactive'] as const)
  .withDefault('active')
  .withOptions({
    clearOnDefault: true,
    history: 'replace'
  });

Type Inference Helper

Utility type for extracting parser return types.

/**
 * Type helper to extract the underlying returned data type of a parser
 * or of an object describing multiple parsers and their associated keys
 */
type inferParserType<Input> =
  Input extends ParserBuilder<any>
    ? inferSingleParserType<Input>
    : Input extends Record<string, ParserBuilder<any>>
      ? inferParserRecordType<Input>
      : never;

Usage Examples:

import { type inferParserType, parseAsInteger, parseAsBoolean } from "nuqs";

const intParser = parseAsInteger.withDefault(0);
const boolParser = parseAsBoolean;

type IntType = inferParserType<typeof intParser>; // number
type BoolType = inferParserType<typeof boolParser>; // boolean | null

const parsers = {
  count: parseAsInteger.withDefault(0),
  enabled: parseAsBoolean,
  name: parseAsString
};

type ParsedValues = inferParserType<typeof parsers>;
// { count: number, enabled: boolean | null, name: string | null }

Install with Tessl CLI

npx tessl i tessl/npm-nuqs

docs

index.md

parsers.md

query-states.md

serialization.md

server-cache.md

tile.json