What if your router caught bugs at compile time instead of production? TanStack Router is the first React router with 100% type-safe navigation, search params, and route context.
The Type Safety Problem
Every React router has the same weakness:
// React Router — typo? Runtime error. Wrong params? Runtime error.
<Link to="/usrs/123" /> // Typo — no error until production
const { id } = useParams(); // id is string | undefined — no guarantees
TanStack Router fixes this:
// TanStack Router — compiler catches everything
<Link to="/usrs/$id" /> // ❌ TypeScript error: route doesn't exist
<Link to="/users/$id" params={{ id: "123" }} /> // ✅ Fully typed
Route Definitions
// routes/users.$userId.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/users/$userId")({
// Validate & parse search params
validateSearch: (search) => ({
page: Number(search.page) || 1,
sort: (search.sort as "name" | "date") || "name",
}),
// Load data before rendering
loader: async ({ params }) => {
// params.userId is typed as string — guaranteed
return fetchUser(params.userId);
},
component: UserPage,
});
function UserPage() {
const { userId } = Route.useParams(); // Typed!
const { page, sort } = Route.useSearch(); // Typed!
const user = Route.useLoaderData(); // Typed!
return <div>{user.name} — Page {page}</div>;
}
Type-Safe Search Params
This is TanStack Router's killer feature:
// Define search params schema per route
validateSearch: z.object({
page: z.number().default(1),
filter: z.enum(["all", "active", "done"]).default("all"),
search: z.string().optional(),
}),
// Now search params are fully typed everywhere
const { page, filter, search } = Route.useSearch();
// page: number, filter: "all" | "active" | "done", search: string | undefined
// Navigate with type-safe search params
navigate({ search: { page: 2, filter: "active" } }); // ✅
navigate({ search: { page: "two" } }); // ❌ TypeScript error
Built-in Data Loading
const route = createFileRoute("/dashboard")({
// Runs before component renders
loader: async () => {
const [stats, notifications] = await Promise.all([
fetchStats(),
fetchNotifications(),
]);
return { stats, notifications };
},
// Pending UI while loading
pendingComponent: () => <Spinner />,
// Error UI
errorComponent: ({ error }) => <ErrorPage error={error} />,
});
vs React Router v7
| Feature | React Router v7 | TanStack Router |
|---|---|---|
| Type-safe params | Partial (generated) | 100% (inferred) |
| Type-safe search params | No | Yes (with validation) |
| Type-safe navigation | No | Yes (compile-time) |
| Bundle size | ~15KB | ~12KB |
| SSR support | Yes | Yes (TanStack Start) |
| Devtools | No | Yes (built-in) |
| Maturity | Battle-tested | Newer but stable |
Getting Started
npm create @tanstack/router@latest
Or add to existing project:
npm install @tanstack/react-router @tanstack/router-plugin
Building complex web applications? I specialize in data-intensive tools and web scraping solutions. Reach out at spinov001@gmail.com or explore my Apify tools.
Top comments (0)