All hooks / Storage and URL
useSearchParam
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.
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]| Parameter | Description |
|---|---|
name | The parameter name. |
serverValue | The 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
| Uses | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS |
|---|---|---|---|---|---|---|
| history.pushStateWidely available | 5+ | 12+ | 4+ | 5+ | 18+ | 4+ |
| popstate eventWidely available | 5+ | 12+ | 4+ | 5+ | 18+ | 4.2+ |
| Navigation APIoptionalNewly available | 102+ | 102+ | 147+ | 26.2+ | 102+ | 26.2+ |
| URLSearchParamsWidely available | 49+ | 17+ | 29+ | 10.1+ | 49+ | 10.3+ |
| URLWidely available | 32+ | 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.