To avoid duplicate state and synchronization bugs, decide first who needs the value and how long it must last. Keep temporary UI state in React; put shareable filters and pagination in the URL; use localStorage for browser-only preferences that survive reloads; and use cookies or a server session when the server must read the value during rendering or an action. Read each value from its chosen source of truth instead of maintaining a second copy.
Where should the state live?
Start with the value’s lifetime, audience, and navigation behavior. These choices are not interchangeable: a URL is visible and shareable, browser storage is client-side, and a cookie can travel with requests so server code can access it.
As an Amazon Associate I earn from qualifying purchases.
| Need | Likely home | What to account for |
|---|---|---|
| Temporary interaction, such as whether a transient panel is open | React component state | Simple and scoped to the component, but lost on refresh or remount. |
| Search, filters, pagination, sort, or selected view that should be shareable | URL search parameters | The URL can be bookmarked, copied, and navigated through browser history. Updates navigate. |
| A browser-only preference that should survive reloads | localStorage |
Persistent on that browser, but unavailable to server rendering and requires client-side initialization and synchronization. |
| State that must be available to the server during rendering or an action | Cookie or server session | Server-accessible and compatible with progressive enhancement, with request/response handling to implement. |
These are starting points, not universal rules. Consider whether the value is sensitive, whether a clean URL matters, whether Back and Forward should revisit changes, whether server rendering needs the value, and whether it must follow a user across devices. Do not put secrets in a URL. Cookie security details depend on the application and deployment; consult current platform and security guidance before choosing cookie settings or storing sensitive values.
Why duplicate state creates glue code
If a filter is in the URL and also in component state, the app must decide which copy wins on initial render, after Back or Forward, after a form submission, and after a reload. Every synchronization path is another chance for the copies to diverge. React’s guidance on managing state emphasizes organizing state so it is not unnecessarily duplicated.
#1 Best Overall
For a value with a natural URL representation, render from the query parameter itself. React Router’s state-management guidance shows how a native form can submit a new query value, letting the URL drive the selected view instead of syncing a separate local variable. React Router describes its approach as “a streamlined solution to state management leading to less code, fresh data, and no state synchronization bugs”; that is the framework’s characterization, not a measured guarantee. See its state-management explanation.
Use React Router’s URL state API carefully
useSearchParams returns the current URLSearchParams and a setter. Calling the setter causes navigation, so query changes are not merely an in-memory state update. The setter accepts strings, objects, arrays of tuples, and URLSearchParams. Consult the useSearchParams API for the installed React Router version.
Compose an update and commit it once
The callback form of the setter resembles React’s state setter, but it does not have React state’s queueing semantics: multiple calls in the same tick do not necessarily build on one another. Compose related changes into one update, then call the setter once.
Free tools Windows power users keep installed
One-click scans. No signup required.
setSearchParams(current => {
const next = new URLSearchParams(current);
next.set("sort", "newest");
next.set("page", "1");
return next;
});
This update sets the sort order and resets pagination as one navigation. Adapt the keys and values to your application’s URL contract.
Rank #3
Do not mutate the returned object on its own
The returned searchParams reference is stable, but the object is mutable. Changing it without calling the setter can make later renders observe values that the browser URL does not contain. Treat it as input: create an updated value and commit it through the setter.
Let forms encode ordinary user choices
When a user submits a search or filter, a form can encode the selected value in the URL. Render from that URL value on the next navigation rather than copying it into local state and writing it back later. This also gives browser navigation a meaningful URL to revisit.
Rank #4
When localStorage or cookies fit better
Use localStorage for client-only persistence
localStorage is suitable for preferences that should remain after a reload but do not need to be present for server rendering. It is not a React state source by itself: the component still needs to initialize from storage and keep any in-memory view consistent with it. React Router’s state-management documentation calls out this synchronization work, the lack of access during server rendering, and the possibility of visual flicker after hydration.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use cookies or a server session when server code needs the value
A cookie or session lets server-side route loaders and actions access state, which can support rendering and progressive enhancement without waiting for browser storage. The trade-off is additional request/response setup and a broader set of code paths that can observe the value. Decide deliberately whether server access is necessary rather than moving every preference into a cookie.
Best Value
Reduce URL parsing and serialization with nuqs
nuqs provides state-like hooks synchronized with query-string keys: useQueryState for a key and useQueryStates for multiple keys. Its parsers can make the expected type explicit, reducing repeated parsing and serialization code. The library documents adapters for Next.js, React SPA, Remix, React Router, TanStack Router, and other environments; setup depends on the framework. Check the current adapter documentation for the application you use.
nuqs removes repetitive mechanics, not product decisions. You still need to choose query keys, parsers, defaults, and how changes interact with browser history and server data. Its options documentation says updates are client-only, replace the current history entry, and do not scroll to the top by default; options can alter history behavior, scrolling, and whether server loaders run.
Defaults have changed across releases: clearOnDefault became true by default in nuqs 2.0, unlike prior 1.x behavior. The server-side documentation identifies loader support as introduced in 2.3.0. Verify these version-sensitive behaviors against the version installed in your project and the adapter you configured.
Quick Recap
A practical implementation sequence
- Classify the value. Decide whether it is temporary UI state, a shareable navigation choice, a browser-only preference, or state the server must read.
- Choose one source of truth. For URL-backed state, read the query parameter to render. Avoid a second component-state copy unless it represents a distinct, temporary draft.
- Define the URL contract. Choose parameter names, valid representations, parsing behavior, and defaults. Consider what should happen when a parameter is missing or invalid.
- Choose navigation behavior. Decide whether an update should create a history entry that Back can revisit or replace the current entry. With React Router, setting search parameters navigates; with nuqs, check its installed-version options.
- Handle coupled values together. For example, when changing a filter invalidates the current page, update both query parameters in one composed setter call.
- Check server and reload behavior. Test direct visits, refreshes, Back and Forward, and server rendering or actions where relevant. If the server must read the value, browser storage alone will not provide it during rendering.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




