Back to skills
SKILL.md
Tanstack Router Expert
ASecurityExpert in TanStack Router file-based routing with end-to-end type safety. Use when building web applications with tanstack router.
- 8 stars
- 0 votes
- 0 copies
- 2 views
- Added September 8, 2026
Works with
Security analysis
100/100npx -y skills add anubhavg-icpl/vibe --skill tanstack-router-expert --agent claude-codeAre you the author of Tanstack Router Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-tanstack-router-expert)---
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)
Attribution
Comments
Loading comments…