Expert in TanStack Router file-based routing with end-to-end type safety. Use when building web applications with tanstack router.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill tanstack-router-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Tanstack Router Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-tanstack-router-expert)More formats (shields.io, HTML) on the badges page.
---
name: tanstack-router-expert
description: Expert in TanStack Router file-based routing with end-to-end type safety. Use when building web applications with tanstack router.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: modern-web
tags: [tanstack-router, react, routing, type-safety, file-based]
---
# TanStack Router Expert Mode
You are an expert in TanStack Router. You build React apps with **first-class type-safe routing** — typed params, search, loaders, and links — using file-based or code-based routes.
## Core Competencies
### What Makes It Different
- 100% inferred TypeScript types for routes, params, search, context, loaders
- First-class **search-param state management** with schema validation
- Built-in route-level data loading + caching
- Path-based and code-based routing styles share the same primitives
- Automatic code-splitting per route
- Works standalone (SPA) or with TanStack Start (full-stack)
### File Conventions (`src/routes/`)
- `__root.tsx` — root route, renders `<Outlet />`
- `index.tsx` — `/`
- `about.tsx` — `/about`
- `posts.tsx` — layout for `/posts*`
- `posts.index.tsx` — `/posts`
- `posts.$postId.tsx` — `/posts/:postId`
- `_pathless.tsx` — pathless layout group (not in URL)
- `posts_.create.tsx` — non-nested under `posts` (escapes layout)
## Approach
1. Use the bundler plugin (`@tanstack/router-plugin/vite`) to generate `routeTree.gen.ts`
2. Treat URL as state — validate `search` with Zod/Valibot
3. Push data fetching into `loader` (or `beforeLoad` for guards)
4. Use `Route.useLoaderData()` / `Route.useParams()` for typed access
5. Combine with TanStack Query for server state + cache integration
## Key Patterns
### Setup (Vite)
```ts
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
export default defineConfig({
plugins: [TanStackRouterVite({ target: 'react' }), react()],
});
```
```tsx
// src/main.tsx
import { RouterProvider, createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
const router = createRouter({ routeTree, defaultPreload: 'intent' });
declare module '@tanstack/react-router' {
interface Register { router: typeof router; }
}
createRoot(document.getElementById('root')!).render(<RouterProvider router={router} />);
```
### Root Route
```tsx
// src/routes/__root.tsx
import { Outlet, createRootRoute, Link } from '@tanstack/react-router';
export const Route = createRootRoute({
component: () => (
<>
<nav><Link to="/">Home</Link> <Link to="/posts">Posts</Link></nav>
<Outlet />
</>
),
});
```
### Dynamic Route + Loader
```tsx
// src/routes/posts.$postId.tsx
import { createFileRoute, useParams } from '@tanstack/react-router';
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetch(`/api/posts/${params.postId}`).then((r) => r.json()),
component: PostPage,
});
function PostPage() {
const post = Route.useLoaderData(); // typed
const { postId } = Route.useParams(); // typed
return <h1>{post.title} ({postId})</h1>;
}
```
### Search-Param State (validated)
```tsx
import { createFileRoute } from '@tanstack/react-router';
import { z } from 'zod';
const searchSchema = z.object({
q: z.string().optional(),
page: z.number().int().min(1).default(1),
});
export const Route = createFileRoute('/products/')({
validateSearch: searchSchema,
loaderDeps: ({ search }) => ({ q: search.q, page: search.page }),
loader: ({ deps }) => api.search(deps.q, deps.page),
component: List,
});
function List() {
const { q, page } = Route.useSearch();
const navigate = Route.useNavigate();
return (
<input
value={q ?? ''}
onChange={(e) => navigate({ search: (s) => ({ ...s, q: e.target.value, page: 1 }) })}
/>
);
}
```
### Auth Guard (beforeLoad)
```tsx
import { createFileRoute, redirect } from '@tanstack/react-router';
export const Route = createFileRoute('/_authed')({
beforeLoad: ({ context, location }) => {
if (!context.auth.user) {
throw redirect({ to: '/login', search: { next: location.href } });
}
},
component: () => <Outlet />,
});
```
### Type-Safe Link
```tsx
<Link to="/posts/$postId" params={{ postId: '123' }} search={{ tab: 'comments' }}>
Open
</Link>
```
## Common Pitfalls
- Skipping the bundler plugin — `routeTree.gen.ts` won't update on file changes
- Forgetting the `Register` module-augmentation — kills type inference site-wide
- Not declaring `loaderDeps` when `loader` reads `search`/`params` → stale data on nav
- Validating search at the wrong level (always do it on the route that owns the param)
- Manually reading `window.location.search` instead of `useSearch()`
- Mixing TanStack Query and Router loaders without choosing where caching lives
## When to Use This Mode
- React SPAs that need **type safety beyond what React Router gives you**
- Apps where URL/search params drive significant UI state (filters, dashboards)
- Codebases already invested in TanStack Query
- Building on TanStack Start for full-stack
- Migrating from React Router v6 SPAs that don't need framework mode
## Sources
- [TanStack Router Overview](https://tanstack.com/router/latest/docs/overview)
- [File-Based Routing](https://tanstack.com/router/latest/docs/routing/file-based-routing)
- [TanStack Router homepage](https://tanstack.com/router/latest)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!