Ryo Tanaka
Writing
TypeScriptTanStack RouterArchitecture

Type-Safe URL State Is Underrated

·Ryo Tanaka

Here's a question: where does the state of your data table live?

Filters, sort column, sort direction, page number, selected rows. If the answer is useState, you're one refresh away from losing it. One copy-paste from being unable to share "this exact view" with a colleague.

URL state is global, persistent, and shareable by definition. It's also the most underused primitive in most React apps.

The typical objection

"Syncing state to the URL is annoying."

It used to be. With raw URLSearchParams it required writing serializers, handling parse failures, keeping multiple sources of truth in sync. The accidental complexity was real.

TanStack Router changes this equation. Search params are now typed — defined once, validated at runtime, and accessed with autocomplete. The "annoying" part is mostly gone.

const Route = createFileRoute('/projects')({
  validateSearch: z.object({
    filter: z.string().optional(),
    sort: z.enum(['date', 'name', 'stars']).default('date'),
    page: z.number().int().min(1).default(1),
  }),
})

function Projects() {
  const { filter, sort, page } = Route.useSearch()
  // ...
}

This is 12 lines instead of a custom hook, a context provider, and a useEffect.

What you get for free

  • Deep linking: users can bookmark filtered views
  • Browser history: back button works as expected
  • Collaboration: share exact table state as a URL
  • Analytics: every state transition is a navigable URL, queryable in your analytics tool

When NOT to use URL state

Not everything belongs in the URL. Ephemeral UI state — is this dropdown open, which tooltip is visible, is this modal animating — has no business being in the URL. The rule I follow: if you'd want the state to survive a refresh, put it in the URL.

Authentication state, user preferences, form drafts — these belong in persistent storage (localStorage, cookies, or server session), not the URL.

The URL is not a catch-all. It's a specific tool for a specific job: state that is both shareable and refreshable.