Fetching latest headlines…
The URL Is Your Best State Manager
NORTH AMERICA
πŸ‡ΊπŸ‡Έ United Statesβ€’June 29, 2026

The URL Is Your Best State Manager

0 views0 likes0 comments
Originally published byDev.to

There's a category of bug I see in almost every SPA I touch: the user applies a filter, sorts a table, pages through results β€” and then refreshes, or pastes the link to a colleague, and all of it is gone. The bug report says "it doesn't remember my state." The root cause is almost always the same: that state lived in useState, when it should have lived in the URL.

This is a mental model, not a framework feature. It applies equally in React, Vue, Svelte, or plain JS. The URL is a state management primitive you already have β€” built into every browser, shared for free β€” and most frontend teams underuse it.

What belongs in the URL

Not all state belongs there. The key question is: would a user want to share or bookmark this?

State URL? Why
Active tab / current view Yes Direct-linkable, shareable
Search query Yes Back button works, shareable
Filters, sort order Yes Reproducible results
Pagination cursor Yes "Send me the page you're on"
Form draft in progress No Ephemeral, user-session only
Hover state, tooltip open No Ephemeral UI state
Auth tokens Never Security hazard

The filter and the modal-open state are both "UI state," but they're different in kind. One the user would share; one they wouldn't. Put the first in the URL and leave the second in component state.

What you gain for free

When search/filter/sort state lives in the URL, you get three things without writing a single extra line of code:

  1. Refresh survival. The user filters a table, refreshes β€” the filter is still there.
  2. Shareable links. "Here's what I'm looking at" works via copy-paste.
  3. Browser history. Back and forward navigate state transitions, which is what users expect.

These are not small wins. "Back button broke my filters" is a real UX bug, and moving to URL state fixes it completely and permanently.

The pattern in React

Here's the before/after for a search input:

// Before: state dies on refresh
function SearchPage() {
  const [query, setQuery] = useState("");
  return (
    <input
      value={query}
      onChange={e => setQuery(e.target.value)}
    />
  );
}
// After: state lives in the URL
import { useSearchParams } from "react-router-dom"; // or Next.js's useSearchParams

function SearchPage() {
  const [params, setParams] = useSearchParams();
  const query = params.get("q") ?? "";

  return (
    <input
      value={query}
      onChange={e =>
        setParams(p => { p.set("q", e.target.value); return p; })
      }
    />
  );
}

The component is roughly the same size. The URL is now ?q=typescript. Refresh β€” still there. Copy the URL β€” the recipient sees the same results. The back button undoes the last keystroke (or use replace instead of push to keep history clean for every keystroke).

For more complex state β€” multiple filters, sort columns, numeric values β€” a library like nuqs handles serialization, type coercion, and history mode in a useState-compatible API:

import { useQueryState, parseAsInteger } from "nuqs";

const [sort, setSort] = useQueryState("sort", { defaultValue: "date" });
const [page, setPage] = useQueryState("page", parseAsInteger.withDefault(1));

Typed URL params, familiar API, zero custom serialization logic.

The failure mode to watch for

Don't put too much in the URL. It's for state the user would meaningfully share β€” not for every piece of transient UI. A deeply nested JSON blob in a query string is the wrong answer. Keep it flat, keep it readable.

?status=active&sort=name&page=2 is good. ?state=eyJzdGF0dXMiOiJhY3RpdmUi... is a red flag.

If your state is too complex to flatten into a few readable params, it may genuinely belong in a database or session store, not the URL. The URL should be a narrow representation of "what the user is looking at" β€” not a full snapshot of the app.

The takeaway

The URL is a first-class state management layer that every browser already implements, every user already understands, and most SPAs leave mostly unused. Before you reach for useState, ask: would the user want to share or bookmark this? If yes, put it in the URL β€” you get refresh survival, deep links, and a working back button for free.

That's not a third-party library or a framework feature. It's how the web was designed. Use it.

Comments (0)

Sign in to join the discussion

Be the first to comment!