When is type-safe routing worth choosing a router for, instead of accepting whatever your meta-framework shipped?
TanStack Router (stable through 2024 into 2025) makes the route tree a typed artifact. Links, params, and search params stop being stringly guesses. That sounds like an obvious win. It is, until the team tax exceeds the bug class you were fixing.
What "type-safe routes" means in practice
I want three compile-time guarantees:
- Params exist:
/invoices/$invoiceIdcannot be linked withoutinvoiceId. - Search params are validated:
?tab=is a known union, notstring. - Path renames hurt at build time: not in a Tuesday on-call when a button 404s.
tsx// src/routes/invoices.$invoiceId.tsx, illustrative TanStack Router shape import { createFileRoute, Link } from '@tanstack/react-router'; export const Route = createFileRoute('/invoices/$invoiceId')({ component: InvoicePage, }); function InvoicePage() { const { invoiceId } = Route.useParams(); return ( <div> <h1>Invoice {invoiceId}</h1> <Link to="/invoices/$invoiceId/edit" params={{ invoiceId }} > Edit </Link> </div> ); }
The Link that omits params should fail typecheck. That is the product.
When it pays off
Reach for TanStack Router when most of these are true:
- You are on Vite + SPA (or a thin SSR shell) and do not need Next/Remix platform features as the center of the app
- Navigation bugs from mistyped paths have already shipped
- Search params encode real UI state (filters, tabs, pagination) that deserve a schema
- You want code-based or generated route trees with first-class types, not only undocumented folder conventions
ts// search params as a contract, illustrative import { z } from 'zod'; import { createFileRoute } from '@tanstack/react-router'; const invoiceSearchSchema = z.object({ tab: z.enum(['activity', 'files', 'billing']).default('activity'), q: z.string().optional(), }); export const Route = createFileRoute('/invoices/')({ validateSearch: (search) => invoiceSearchSchema.parse(search), component: InvoicesIndex, });
Validated search params turn the URL into API. Teams that already treat the URL as state feel at home; teams that stash filters only in Zustand will not feel the win until they move state back into the address bar.
When framework defaults are enough
Stay with Next App Router, Remix, or React Router framework mode when:
- You need RSC, adapters, or platform deploy as the product spine
- Your route count is small and links are few
- The team already thinks in loaders/actions and the bug class is data, not path typos
- You would adopt TanStack Router only because a Twitter thread called it "modern"
React Router remains a strong default for SPA navigation. Remix/RR framework paths keep HTTP semantics close. Next owns the RSC-shaped app. TanStack Router competes hardest where TypeScript-first SPA navigation is the differentiator
- not where you are shopping for a full-stack framework.
File mode vs code mode
TanStack Router can generate from files or build the tree in code. I pick based on team shape:
- File mode when many engineers add routes weekly and want folder discoverability (same social benefit as Next/Remix files).
- Code mode when routes are generated, multi-tenant, or few enough that a
single
routeTreemodule is clearer than empty directories.
Either way the typed route tree is the product, files are optional sugar on top of that guarantee. If file mode becomes tribal knowledge (special filenames nobody can explain), drop back to an explicit tree until the convention is documented.
Fit beside TanStack Query
Router owns location. Query owns server cache. Do not merge them into one abstraction because both logos say TanStack.
| Concern | Owner |
|---|---|
| URL, params, search, nested matches | Router |
| Fetched remote data, stale time, retries | Query |
| "Open invoice 99, tab=files" | Router search + params |
| "Invoice 99 payload, refreshed after edit" | Query keys + invalidation |
TanStack Query is the matching handoff: typed routes make keys and deep-links honest; Query makes the network honest.
Failure modes
Types without discipline. A generated route tree nobody understands, plus
as any on every non-trivial link.
Search-param everything. Putting ephemeral hover state in the URL because "type-safe."
Fighting the meta-framework. Running TanStack Router inside Next App Router as a second source of truth for URLs, usually a cry for help.
Ignoring loaders you already had. Replacing Remix/RR loaders with ad-hoc effects "because TanStack" deletes progressive enhancement without gaining types.
Skipping validateSearch. You bought a type-safe router and left ?tab as
string, congratulations on the merchandise.
Continuity
Type-safe navigation is a scalpel. TanStack Query is the matching scalpel for server state: cache keys, invalidation, and the boundary with client state that routing alone cannot fix.
Takeaway
TanStack Router pays off when mistyped paths and untyped search params are real bugs and you own a TypeScript-first SPA. It is not a moral upgrade over Next or Remix, pick it when the route tree deserves to be a typed API.