# Tanstack Router

> Type-safe client-side router with search params, loaders, and file-based routing

- Skill: `ultroncore/tanstack-router` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ultroncore/tanstack-router`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ultroncore/tanstack-router/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: UltronCore (https://skillmd.com/u/ultroncore)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ultroncore/tanstack-router

---


# TanStack Router — Type-Safe Routing

## Overview
TanStack Router is a fully type-safe client-side router for React (and Solid). Every route, param, search param, and loader data is typed end-to-end. It supports file-based routing, built-in data loading with caching, search param schemas, and nested layouts — without needing a meta-framework.

## When to Use
- SPAs that need type-safe routing (React Router doesn't provide full type safety)
- Apps where search params carry complex structured state
- React apps wanting data loading with caching similar to Next.js without going full SSR
- Replacing React Router in a large TypeScript codebase
- Building a SPA but wanting Remix-like loader/action patterns

## Installation / Setup

```bash
npm install @tanstack/react-router
# File-based routing (optional):
npm install -D @tanstack/router-plugin @tanstack/router-devtools
```

```ts
// vite.config.ts (file-based routing)
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [TanStackRouterVite(), react()],
});
```

## Key Patterns

### File-Based Route Convention
```
src/routes/
  __root.tsx          → Root layout
  index.tsx           → /
  about.tsx           → /about
  posts/
    index.tsx         → /posts
    $postId.tsx       → /posts/:postId
  _auth/              → Pathless layout (auth wrapper)
    dashboard.tsx     → /dashboard
```

### Root Route
```tsx
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>
        <Link to="/" activeProps={{ className: 'active' }}>Home</Link>
        <Link to="/posts" activeProps={{ className: 'active' }}>Posts</Link>
      </nav>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
});
```

### Route with Loader and Params
```tsx
// src/routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    // params.postId is typed as string
    const post = await fetchPost(params.postId);
    return { post };
  },
  component: function Post() {
    const { post } = Route.useLoaderData(); // fully typed
    const { postId } = Route.useParams();   // typed as { postId: string }
    return <article><h1>{post.title}</h1></article>;
  },
});
```

### Type-Safe Search Params
```tsx
// src/routes/users/index.tsx
import { createFileRoute } from '@tanstack/react-router';
import { z } from 'zod';

const searchSchema = z.object({
  page: z.number().catch(1),
  q: z.string().optional(),
  sort: z.enum(['asc', 'desc']).catch('asc'),
});

export const Route = createFileRoute('/users/')({
  validateSearch: searchSchema,
  loader: async ({ location: { search } }) => {
    return fetchUsers({ page: search.page, query: search.q, sort: search.sort });
  },
  component: function Users() {
    const search = Route.useSearch(); // typed: { page: number; q?: string; sort: 'asc' | 'desc' }
    const navigate = Route.useNavigate();

    return (
      <div>
        <input
          value={search.q ?? ''}
          onChange={e => navigate({ search: { ...search, q: e.target.value, page: 1 } })}
        />
      </div>
    );
  },
});
```

### Code-Split Routes (Manual)
```ts
// src/routeTree.gen.ts is auto-generated by the plugin
// For manual setup:
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';

const router = createRouter({ routeTree });

declare module '@tanstack/react-router' {
  interface Register { router: typeof router; }
}
```

### Authentication Guard
```tsx
// src/routes/_auth.tsx (pathless layout)
import { createFileRoute, redirect } from '@tanstack/react-router';

export const Route = createFileRoute('/_auth')({
  beforeLoad: async ({ context }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({ to: '/login', search: { redirect: location.href } });
    }
  },
  component: () => <Outlet />,
});
```

## Common Pitfalls
- **Regenerate routeTree after adding files**: the plugin auto-generates `routeTree.gen.ts` — restart dev server if routes aren't found
- **`catch()` in search schemas is required**: without `catch`, invalid URL params throw instead of defaulting
- **`useSearch` returns the whole search, not just the route's schema**: use `Route.useSearch()` (not `useSearch()`) for scoped type safety
- **Loader data is refetched on navigation**: unlike SWR/React Query, loaders run on each navigation — add `staleTime` or integrate React Query for caching
- **Type inference requires TypeScript 5+**: older TS versions may struggle with the deep inference patterns

## Related Skills
- `tanstack-form` — the form library from the same ecosystem
- `react-query-tanstack` — data fetching that pairs with TanStack Router loaders
- `zod-expert` — search param validation
- `react-best-practices` — React fundamentals

## GitNexus Index
```
domain: frontend/web
tier: library
runtime: browser
language: tsx,ts
framework: react,solid
purpose: routing
```

