All hooks / Storage and URL

useSearchParam

310 B minified and brotliedSourceMarkdown✓ Works in all modern browsers

One URL query parameter (?tab=billing) as state: its value, or null when it's missing, and a setter that updates the URL without reloading. Works with or without a router: re-renders on set (in any component), Back and Forward, and router navigation that uses history.pushState (in browsers with the Navigation API).

The server render and hydration use serverValue, then the real URL with no hydration mismatch. Pass the request's value (e.g. from searchParams in a Next.js page) so the first frame is already right.

Works with Next.js's App Router: after set, Next's own useSearchParams updates too, and no server request is made.

Import

import { useSearchParam } from '@vincent.ri/react-tils'
// or only this hook
import { useSearchParam } from '@vincent.ri/react-tils/useSearchParam'

Usage

const [query, setQuery] = useSearchParam('q')
return <input value={query ?? ''} onChange={(e) => setQuery(e.target.value || null)} />

// Tabs where Back returns to the previous tab
const [tab, setTab] = useSearchParam('tab')
return <button onClick={() => setTab('billing', { push: true })}>Billing</button>

Live demo

Type, pick a sort, then reload or press Back.

q: (none) · sort: (none)

When to use it

State that should be linkable, bookmarkable, and survive reload: the selected tab, a search query, filters, sorting, a page number, an open item's id.

When not to

Your router already gives you this (React Router's useSearchParams); for state that shouldn't reach the server, use useHash; for private or large state, use useLocalStorage. Values are strings: parse numbers and validate, since anyone can edit the URL.

API

function useSearchParam(name: string, serverValue?: string | null): [string | null, (value: string | null, options?: {
  push?: boolean;
}) => void]
ParameterDescription
nameThe parameter name.
serverValueThe value on the server and during hydration. Default null.

Returns [value, set]. set(value) replaces the current history entry; set(value, { push: true }) adds one, so Back undoes it. set(null) removes the parameter.

Browser support

UsesChromeEdgeFirefoxSafariChrome AndroidSafari iOS
history.pushStateWidely available5+12+4+5+18+4+
popstate eventWidely available5+12+4+5+18+4.2+
Navigation APIoptionalNewly available102+102+147+26.2+102+26.2+
URLSearchParamsWidely available49+17+29+10.1+49+10.3+
URLWidely available32+12+19+7+32+7+
  • Router navigation that changes the URL with history.pushState (e.g. Next.js <Link>) is noticed only where the Navigation API exists. Elsewhere the value still updates on Back and Forward and through the hook's own setter.

Optional features are used when present; without them the hook still works.

First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).

Coming from another library?

Other libraries call this useQueryState (nuqs), useSearchParam (react-use), useUrlState.