Type-safe, file-based routing for React SPAs with route-level data loading and TanStack Query integration ---
Scanned 2/12/2026
Install via CLI
openskills install jezweb/claude-skills---
name: TanStack Router
description: |
Build type-safe, file-based React routing with TanStack Router. Supports client-side navigation, route loaders, and TanStack Query integration.
Use when implementing file-based routing patterns, building SPAs with TypeScript routing, or troubleshooting devtools dependency errors, type safety issues, or Vite bundling problems.
---
# TanStack Router
Type-safe, file-based routing for React SPAs with route-level data loading and TanStack Query integration
---
## Quick Start
**Last Updated**: 2025-11-28
**Version**: @tanstack/react-router@1.139.10
```bash
npm install @tanstack/react-router @tanstack/router-devtools
npm install -D @tanstack/router-plugin
```
**Vite Config** (TanStackRouterVite MUST come before react()):
```typescript
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [TanStackRouterVite(), react()], // Order matters!
})
```
**File Structure**:
```
src/routes/
├── __root.tsx → createRootRoute() with <Outlet />
├── index.tsx → createFileRoute('/')
└── posts.$postId.tsx → createFileRoute('/posts/$postId')
```
**App Setup**:
```typescript
import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen' // Auto-generated by plugin
const router = createRouter({ routeTree })
<RouterProvider router={router} />
```
---
## Core Patterns
**Type-Safe Navigation** (routes auto-complete, params typed):
```typescript
<Link to="/posts/$postId" params={{ postId: '123' }} />
<Link to="/invalid" /> // ❌ TypeScript error
```
**Route Loaders** (data fetching before render):
```typescript
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => ({ post: await fetchPost(params.postId) }),
component: ({ useLoaderData }) => {
const { post } = useLoaderData() // Fully typed!
return <h1>{post.title}</h1>
},
})
```
**TanStack Query Integration** (prefetch + cache):
```typescript
const postOpts = (id: string) => queryOptions({
queryKey: ['posts', id],
queryFn: () => fetchPost(id),
})
export const Route = createFileRoute('/posts/$postId')({
loader: ({ context: { queryClient }, params }) =>
queryClient.ensureQueryData(postOpts(params.postId)),
component: () => {
const { postId } = Route.useParams()
const { data } = useQuery(postOpts(postId))
return <h1>{data.title}</h1>
},
})
```
---
## Known Issues & Solutions
**Issue #1: Devtools Dependency Resolution**
- **Error**: Build fails with `@tanstack/router-devtools-core` not found
- **Fix**: `npm install @tanstack/router-devtools`
**Issue #2: Vite Plugin Order** (CRITICAL)
- **Error**: Routes not auto-generated, `routeTree.gen.ts` missing
- **Fix**: TanStackRouterVite MUST come before react() in plugins array
- **Why**: Plugin processes route files before React compilation
**Issue #3: Type Registration Missing**
- **Error**: `<Link to="...">` not typed, no autocomplete
- **Fix**: Import `routeTree` from `./routeTree.gen` in main.tsx to register types
**Issue #4: Loader Not Running**
- **Error**: Loader function not called on navigation
- **Fix**: Ensure route exports `Route` constant: `export const Route = createFileRoute('/path')({ loader: ... })`
**Issue #5: Memory Leak with TanStack Form**
- **Error**: Production crashes when using TanStack Form + Router
- **Source**: GitHub Issue #5734 (known issue, unfixed as of v1.139)
- **Workaround**: Use React Hook Form or Formik instead
---
## Cloudflare Workers Integration
**Vite Config** (add @cloudflare/vite-plugin):
```typescript
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [TanStackRouterVite(), react(), cloudflare()],
})
```
**API Routes Pattern** (fetch from Workers backend):
```typescript
// Worker: functions/api/posts.ts
export async function onRequestGet({ env }) {
const { results } = await env.DB.prepare('SELECT * FROM posts').all()
return Response.json(results)
}
// Router: src/routes/posts.tsx
export const Route = createFileRoute('/posts')({
loader: async () => fetch('/api/posts').then(r => r.json()),
})
```
---
**Related Skills**: tanstack-query (data fetching), cloudflare-worker-base (API backend), tailwind-v4-shadcn (UI)
No comments yet. Be the first to comment!