nuqs
Type-safe URL search params as React state — keep filters, sorting, and pagination in the URL.
nuqs
ReactType-safe URL search params as React state — keep filters, sorting, and pagination in the URL.
Fit
Bundle (gzip)
6.3 kB
17.8 kB raw
Install size includes transitive dependencies. Bundle size is gzipped browser payload.
Freshness
The Problem
Building a page with filters, sorting, or pagination that survives a browser refresh is harder than it should be. The usual approaches are:
- Local state (
useState) — filters reset on refresh and can't be shared via URL - Manual
URLSearchParamsparsing — verbose, error-prone, no type safety - Redux/Zustand + URL sync — overkill for this use case, lots of boilerplate
The result: users can't bookmark a filtered view, share a link to specific results, or use the browser back button naturally.
What It Does
nuqs gives you a useQueryState hook — a drop-in replacement for useState that reads from and writes to the URL query string. It's type-safe, supports serialization of complex types (numbers, booleans, arrays, dates), and works with Next.js App Router, Pages Router, and other React frameworks.
With ~300K weekly npm downloads, it's widely used but rarely talked about.
Installation
npm install nuqsUsage Example
Basic string search param
"use client";
import { useQueryState } from "nuqs";
export function SearchInput() {
const [query, setQuery] = useQueryState("q");
return (
<input
value={query ?? ""}
onChange={(e) => setQuery(e.target.value || null)}
placeholder="Search..."
/>
);
// URL becomes: /products?q=shoes
}Typed params with parsers (number, boolean, array)
"use client";
import { useQueryState, parseAsInteger, parseAsBoolean, parseAsArrayOf, parseAsString } from "nuqs";
export function ProductFilters() {
const [page, setPage] = useQueryState("page", parseAsInteger.withDefault(1));
const [inStock, setInStock] = useQueryState("inStock", parseAsBoolean.withDefault(false));
const [tags, setTags] = useQueryState("tags", parseAsArrayOf(parseAsString).withDefault([]));
return (
<div>
<button onClick={() => setPage(page + 1)}>Page {page}</button>
<label>
<input
type="checkbox"
checked={inStock}
onChange={(e) => setInStock(e.target.checked)}
/>
In stock only
</label>
</div>
);
// URL becomes: /products?page=2&inStock=true&tags=sale,new
}Server-side parsing in Next.js App Router
// app/products/page.tsx (Server Component)
import { createSearchParamsCache, parseAsInteger, parseAsString } from "nuqs/server";
const searchParamsCache = createSearchParamsCache({
q: parseAsString.withDefault(""),
page: parseAsInteger.withDefault(1),
});
export default async function ProductsPage({ searchParams }) {
const { q, page } = searchParamsCache.parse(searchParams);
const products = await fetchProducts({ query: q, page });
return <ProductList products={products} />;
}Related packages
Fast, composable command palette for React — keyboard-driven search UI with fuzzy filtering and accessible markup out of the box.
@dnd-kit/coreReactModern, accessible drag-and-drop toolkit for React — fully customizable with keyboard support, collision detection, and zero dependencies.
embla-carousel-reactReactLightweight, extensible carousel library for React — smooth scrolling, touch/mouse drag, and full CSS control without prescribing markup.
flagsReactProvider-agnostic feature flags SDK for Next.js and React — toggle features without redeploying.
framer-motionReactProduction-ready declarative animation library for React — gestures, layout animations, and scroll-driven effects with a clean API.