Type-safe search params state manager for Next.js - Like React.useState, but stored in the URL query string
npx @tessl/cli install tessl/npm-nuqs@1.20.0nuqs is a type-safe state management library for Next.js that synchronizes React component state with URL query parameters. It provides React hooks that work like useState but store values in the URL query string, enabling shareable URLs and maintaining state across page refreshes.
npm install nuqsimport { useQueryState, useQueryStates, parseAsInteger } from "nuqs";For server-side usage:
import { createSearchParamsCache, parseAsString } from "nuqs/server";import { useQueryState, parseAsInteger } from "nuqs";
export default function SearchPage() {
const [search, setSearch] = useQueryState('q');
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
return (
<div>
<input
value={search || ''}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
<p>Current page: {page}</p>
<button onClick={() => setPage(page + 1)}>Next Page</button>
</div>
);
}nuqs is built around several key components:
useQueryState and useQueryStates provide React state management synchronized with URL query parameterscreateSearchParamsCacheCore hook for managing individual query parameters with type safety and default values.
function useQueryState<T>(
key: string,
options?: UseQueryStateOptions<T>
): UseQueryStateReturn<T, undefined>;
function useQueryState<T>(
key: string,
options: UseQueryStateOptions<T> & { defaultValue: T }
): UseQueryStateReturn<T, T>;
interface UseQueryStateOptions<T> extends Parser<T>, Options {}
type UseQueryStateReturn<Parsed, Default> = [
Default extends undefined ? Parsed | null : Parsed,
(value: Parsed | null | ((old: Parsed | null) => Parsed | null)) => Promise<URLSearchParams>
];Hook for managing multiple related query parameters as a single state object.
function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
keyMap: KeyMap,
options?: UseQueryStatesOptions
): UseQueryStatesReturn<KeyMap>;
type UseQueryStatesKeysMap<Map = any> = {
[Key in keyof Map]: KeyMapValue<Map[Key]>;
};
type UseQueryStatesReturn<T extends UseQueryStatesKeysMap> = [
Values<T>,
SetValues<T>
];Built-in parsers for common data types with fluent API for configuration and default values.
const parseAsString: ParserBuilder<string>;
const parseAsInteger: ParserBuilder<number>;
const parseAsHex: ParserBuilder<number>;
const parseAsFloat: ParserBuilder<number>;
const parseAsBoolean: ParserBuilder<boolean>;
const parseAsTimestamp: ParserBuilder<Date>;
const parseAsIsoDateTime: ParserBuilder<Date>;
function parseAsStringEnum<Enum extends string>(validValues: Enum[]): ParserBuilder<Enum>;
function parseAsStringLiteral<Literal extends string>(validValues: readonly Literal[]): ParserBuilder<Literal>;
function parseAsNumberLiteral<Literal extends number>(validValues: readonly Literal[]): ParserBuilder<Literal>;
function parseAsArrayOf<ItemType>(itemParser: Parser<ItemType>, separator?: string): ParserBuilder<ItemType[]>;
function parseAsJson<T>(parser?: (value: unknown) => T): ParserBuilder<T>;
interface ParserBuilder<T> extends Required<Parser<T>> {
withDefault(defaultValue: NonNullable<T>): ParserBuilder<T> & { defaultValue: NonNullable<T> };
withOptions(options: Options): ParserBuilder<T>;
}Server-side utilities for accessing and parsing query parameters in React Server Components.
function createSearchParamsCache<Parsers extends Record<string, ParserBuilder<any>>>(
parsers: Parsers
): {
parse(searchParams: SearchParams): ParsedSearchParams;
get<Key extends keyof Parsers>(key: Key): ParsedSearchParams[Key];
all(): ParsedSearchParams;
};
type SearchParams = Record<string, string | string[] | undefined>;Utilities for generating query strings from typed values using parser configuration.
function createSerializer<Parsers extends Record<string, ParserBuilder<any>>>(
parsers: Parsers
): SerializerFunction<Parsers>;
interface SerializerFunction<Parsers extends Record<string, ParserBuilder<any>>> {
(values: Values<Parsers>): string;
(base: string | URLSearchParams | URL, values: Values<Parsers> | null): string;
}
type Values<Parsers extends Record<string, ParserBuilder<any>>> = Partial<{
[K in keyof Parsers]?: ExtractParserType<Parsers[K]>;
}>;
type ExtractParserType<Parser> = Parser extends ParserBuilder<any>
? ReturnType<Parser['parseServerSide']>
: never;Deprecated synchronization utility for listening to URL query changes.
/**
* @deprecated Since Next.js introduced shallow routing in 14.0.3, this method is no longer needed.
* This method will be removed in nuqs@2.0.0.
*/
function subscribeToQueryUpdates(
callback: (args: QueryUpdateNotificationArgs) => void
): () => void;
type QueryUpdateSource = 'internal' | 'external';
interface QueryUpdateNotificationArgs {
search: URLSearchParams;
source: QueryUpdateSource;
}Deprecated APIs maintained for backward compatibility.
/**
* @deprecated renamed to Parser
*/
type Serializers<T> = Parser<T>;
/**
* @deprecated renamed to ParserBuilder. You should probably use createParser instead.
*/
type SerializersWithDefaultFactory<T> = ParserBuilder<T>;
/**
* @deprecated use individual parseAsXyz imports instead.
*/
const queryTypes: {
readonly string: ParserBuilder<string>;
readonly integer: ParserBuilder<number>;
readonly float: ParserBuilder<number>;
readonly boolean: ParserBuilder<boolean>;
readonly timestamp: ParserBuilder<Date>;
readonly isoDateTime: ParserBuilder<Date>;
readonly stringEnum: typeof parseAsStringEnum;
readonly json: typeof parseAsJson;
readonly array: typeof parseAsArrayOf;
};
/**
* @deprecated use individual parseAsXyz imports instead
*/
type QueryTypeMap = typeof queryTypes;interface Parser<T> {
/** Convert a query string value into a state value */
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 */
eq?: (a: T, b: T) => boolean;
}
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 HistoryOptions = 'replace' | 'push';
type TransitionStartFunction = (callback: () => void) => void;
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};