Tanstack router
Skill ComeOnOliver/skillshub/skills/TerminalSkills/skills/tanstack-router
π§ The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.From the repository description
npx -y skills add ComeOnOliver/skillshub --skill tanstack-routerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
7.1 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
TanStack Router
Overview
TanStack Router is a fully type-safe router for React. Every route path, search param, path param, and loader is typed end-to-end β if you change a route, TypeScript catches every broken link at compile time. File-based routing with automatic code splitting, validated search params, and route-level data loading.
When to Use
- React SPA or SSR app that needs type-safe routing (links, params, search)
- Migrating from React Router and want compile-time route safety
- Need validated and typed search/query params (not just
string | undefined) - Route-level data loading with pending/error states
- File-based routing with automatic code splitting
Instructions
Setup
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin # Vite plugin for file-based routing
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
export default defineConfig({
plugins: [TanStackRouterVite(), react()],
});
File-Based Routing
src/routes/
βββ __root.tsx # Root layout (wraps all pages)
βββ index.tsx # /
βββ about.tsx # /about
βββ users/
β βββ index.tsx # /users
β βββ $userId.tsx # /users/:userId (dynamic param)
β βββ $userId/
β βββ posts.tsx # /users/:userId/posts (nested)
βββ settings/
βββ _layout.tsx # Layout wrapper for /settings/*
βββ profile.tsx # /settings/profile
βββ billing.tsx # /settings/billing
Root Layout
// src/routes/__root.tsx
import { createRootRoute, Outlet, Link } from "@tanstack/react-router";
export const Route = createRootRoute({
component: () => (
<div>
<nav className="flex gap-4 p-4 border-b">
<Link to="/" className="[&.active]:font-bold">Home</Link>
<Link to="/users" className="[&.active]:font-bold">Users</Link>
<Link to="/about" className="[&.active]:font-bold">About</Link>
</nav>
<main className="p-4">
<Outlet />
</main>
</div>
),
});
Route with Loader
// src/routes/users/index.tsx β Route with data loading
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
// Search params schema β validated and typed
const usersSearchSchema = z.object({
page: z.number().int().positive().catch(1),
search: z.string().optional(),
role: z.enum(["all", "admin", "user"]).catch("all"),
});
export const Route = createFileRoute("/users/")({
// Validate search params with Zod
validateSearch: usersSearchSchema,
// Load data before rendering (with typed search params)
loaderDeps: ({ search }) => ({ search }),
loader: async ({ deps: { search } }) => {
const params = new URLSearchParams({
page: String(search.page),
...(search.search && { search: search.search }),
...(search.role !== "all" && { role: search.role }),
});
const res = await fetch(`/api/users?${params}`);
return res.json() as Promise<{ users: User[]; total: number }>;
},
component: UsersPage,
});
function UsersPage() {
const { users, total } = Route.useLoaderData();
const { page, search, role } = Route.useSearch();
const navigate = Route.useNavigate();
return (
<div>
<h1>Users ({total})</h1>
<input
value={search ?? ""}
onChange={(e) => navigate({ search: { search: e.target.value, page: 1 } })}
placeholder="Search users..."
/>
<select
value={role}
onChange={(e) => navigate({ search: { role: e.target.value as any, page: 1 } })}
>
<option value="all">All roles</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
{users.map((user) => (
<Link key={user.id} to="/users/$userId" params={{ userId: user.id }}>
{user.name}
</Link>
))}
<button
disabled={page <= 1}
onClick={() => navigate({ search: { page: page - 1 } })}
>
Previous
</button>
<button onClick={() => navigate({ search: { page: page + 1 } })}>
Next
</button>
</div>
);
}
Dynamic Route Params
// src/routes/users/$userId.tsx β Dynamic route with typed params
import { createFileRoute, notFound } from "@tanstack/react-router";
export const Route = createFileRoute("/users/$userId")({
loader: async ({ params: { userId } }) => {
// userId is typed as string β no casting needed
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw notFound();
return res.json() as Promise<User>;
},
notFoundComponent: () => <div>User not found</div>,
component: UserProfile,
});
function UserProfile() {
const user = Route.useLoaderData();
// ^? User β fully typed from loader return
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<Link to="/users/$userId/posts" params={{ userId: user.id }}>
View Posts
</Link>
</div>
);
}
Type-Safe Links
// Links are fully typed β wrong routes or missing params = compile error
import { Link } from "@tanstack/react-router";
// β
Correct β route exists, params match
<Link to="/users/$userId" params={{ userId: "123" }}>Profile</Link>
// β
Search params typed
<Link to="/users" search={{ page: 2, role: "admin" }}>Admin Users</Link>
// β Compile error β route doesn't exist
<Link to="/nonexistent">Broken</Link>
// β Compile error β missing required param
<Link to="/users/$userId">Missing userId</Link>
Examples
Example 1: Dashboard with filtered data views
User prompt: "Build a dashboard with users list that supports search, pagination, and role filtering β all in the URL."
The agent will set up TanStack Router with validated search params (page, search, role), route-level loader that fetches filtered data, and type-safe navigation that preserves filter state in the URL.
Example 2: Nested layouts for settings
User prompt: "Create a settings page with sidebar navigation β profile, billing, and team sections."
The agent will create a settings layout route with sidebar Links, nested routes for each section, and loaders for settings data.
Guidelines
- Search params = state β use URL search params instead of React state for filterable/bookmarkable views
- Validate search params with Zod β
.catch()provides defaults for invalid params - Loaders run before render β no loading spinners for route-level data
notFound()in loaders β throw it to render the notFoundComponent- Links are type-checked β changing a route path catches all broken links at compile time
- File naming = route structure β
$paramfor dynamic segments,_layoutfor layout routes - Code splitting is automatic β each route file becomes a separate chunk
loaderDepscontrols re-fetching β loader re-runs only when deps change- TanStack Router + TanStack Query β use together for server state + route state
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.