Skip to main content
ClaudeWave
Skill282 estrellas del repoactualizado 3mo ago

tanstack-router

This Claude Code skill provides patterns for implementing TanStack Router, a type-safe file-based routing library for React applications. Use it when setting up new routing systems, configuring route parameters and search validation, organizing route hierarchies with layouts, or implementing route loaders and navigation logic in TanStack Router v1.x+ projects.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/MadAppGang/claude-code /tmp/tanstack-router && cp -r /tmp/tanstack-router/plugins/dev/skills/frontend/tanstack-router ~/.claude/skills/tanstack-router
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# TanStack Router Patterns

Type-safe, file-based routing for React applications with TanStack Router.

## Installation

```bash
pnpm add @tanstack/react-router
pnpm add -D @tanstack/router-plugin
```

```typescript
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    react(),
    TanStackRouterVite(), // Generates route tree
  ],
})
```

## Automatic Code Splitting (Recommended)

**TanStack Router v1.x+ (2025)** introduces automatic code splitting that separates critical route configuration from non-critical components.

```typescript
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    react(),
    TanStackRouterVite({
      autoCodeSplitting: true, // NEW: Enable automatic splitting
    }),
  ],
})
```

**What Gets Split:**

| Critical (Always Loaded) | Non-Critical (Lazy Loaded) |
|--------------------------|---------------------------|
| Route configuration | Component |
| Loaders | Error component |
| Search params validation | Pending component |
| beforeLoad | Not-found component |

**Benefits:**
- Smaller initial bundle (route config without components)
- Automatic optimization (no manual `.lazy.tsx` files needed)
- Better perceived performance (loaders start immediately)

**When to Use:**
- **Recommended for all new projects**
- Existing projects: Enable and test bundle sizes
- Large apps benefit most (many routes)

## Bootstrap

```typescript
// src/main.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

// Register router for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

ReactDOM.createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>
)
```

## File-Based Routes

```
src/routes/
├── __root.tsx                 # Root layout (Outlet, providers)
├── index.tsx                  # "/" route
├── about.tsx                  # "/about" route
├── users/
│   ├── index.tsx              # "/users" route
│   └── $userId.tsx            # "/users/:userId" route (dynamic)
└── posts/
    ├── $postId/
    │   ├── index.tsx          # "/posts/:postId" route
    │   └── edit.tsx           # "/posts/:postId/edit" route
    └── index.tsx              # "/posts" route
```

**Naming Conventions:**
- `__root.tsx` - Root layout (contains `<Outlet />`)
- `index.tsx` - Index route for that path
- `$param.tsx` - Dynamic parameter (e.g., `$userId` → `:userId`)
- `_layout.tsx` - Layout route (no URL segment)
- `route.lazy.tsx` - Lazy-loaded route

## Virtual File Routes

Virtual file routes allow the router to auto-generate route anchors without physical files:

```
src/routes/
├── __root.tsx           # Root layout
├── index.tsx            # "/" (physical file)
├── about.lazy.tsx       # "/about" (virtual route, lazy only)
└── users/
    ├── index.tsx        # "/users" (physical)
    └── $userId.lazy.tsx # "/users/:userId" (virtual, lazy only)
```

**Key Insight:** If you only need a component (no loader, no search validation), you can delete the base route file. The router auto-generates a virtual anchor for `.lazy.tsx` files.

**Example - Minimal About Page:**
```typescript
// src/routes/about.lazy.tsx
// No about.tsx needed! Router creates virtual anchor
import { createLazyFileRoute } from '@tanstack/react-router'

export const Route = createLazyFileRoute('/about')({
  component: () => <div>About Us</div>,
})
```

**When Virtual Routes Make Sense:**
- Static pages with no data fetching
- Simple UI components
- When you want maximum code splitting

## Root Layout

```typescript
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/users">Users</Link>
      </nav>

      <main>
        <Outlet /> {/* Child routes render here */}
      </main>

      <TanStackRouterDevtools /> {/* Auto-hides in production */}
    </>
  ),
})
```

## Basic Route

```typescript
// src/routes/about.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/about')({
  component: AboutComponent,
})

function AboutComponent() {
  return <div>About Page</div>
}
```

## Dynamic Routes with Params

```typescript
// src/routes/users/$userId.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/users/$userId')({
  component: UserComponent,
})

function UserComponent() {
  const { userId } = Route.useParams() // Fully typed!

  return <div>User ID: {userId}</div>
}
```

## Typed Search Params

```typescript
// src/routes/users/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

const userSearchSchema = z.object({
  page: z.number().default(1),
  filter: z.enum(['active', 'inactive', 'all']).default('all'),
  search: z.string().optional(),
})

export const Route = createFileRoute('/users/')({
  validateSearch: userSearchSchema,
  component: UsersComponent,
})

function UsersComponent() {
  const { page, filter, search } = Route.useSearch() // Fully typed!

  return (
    <div>
      <p>Page: {page}</p>
      <p>Filter: {filter}</p>
      {search && <p>Search: {search}</p>}
    </div>
  )
}
```

## Navigation with Link

```typescript
import { Link } from '@tanstack/react-router'

// Basic navigation
<Link to="/about">About