Make URL search params the source of truth with TanStack Router and Query

Today I Learned · August 2, 2026

Make URL search params the source of truth with TanStack Router and Query

Stop duplicating state between the URL and component state. With TanStack Router

The pieces

Piece Role
validateSearch Declares the search-param schema, so params are typed and validated
loaderDeps Returns a value derived from the search params that the loader depends on
ensureQueryData In the loader: return cached data if fresh, otherwise fetch and wait
navigate() The only way to change params — call it with a new search object
Nested routes A modal/sheet rendered as a child route inside the parent’s <Outlet />

How it flows

  1. State lives in the URL: ?q=foo&page=2.
  2. The loader’s loaderDeps derive from those params. When the deps change, the loader reruns automatically.
  3. The loader calls queryClient.ensureQueryData(...) — the route only renders once the data for the current params actually exists in the cache.
  4. Changing the params is just navigate({ search: (prev) => ({ ...prev, page: 2 }) }). The router reruns the loader; nothing else needs to know.
  5. Modals and sheets become nested child routes. “Open” = navigate() to the child; “close” = navigate() back to the parent. No useState modal flags.

Why it wins

Gotchas