Claude Code for TanStack Router — Workflow Guide

Written by Michael Lip · Solo founder of Zovo · $400K+ on Upwork · 100% JSS Join 50+ builders · More at zovo.one

The Setup

You are building a React application with TanStack Router, the fully type-safe router that provides compile-time route validation, search param schemas, and file-based routing. Claude Code can generate routes and navigation, but it defaults to React Router v6 patterns that are structurally different.

What Claude Code Gets Wrong By Default

  1. Uses React Router’s <Route> JSX syntax. Claude writes <Route path="/users/:id" element={<UserPage />} />. TanStack Router defines routes as a tree using createFileRoute or createRoute with configuration objects.

  2. Ignores search parameter typing. Claude reads search params with useSearchParams() returning untyped strings. TanStack Router validates search params with Zod or Valibot schemas, giving you typed, validated search parameters.

  3. Uses useNavigate() with string paths. Claude writes navigate('/users/123') with raw strings. TanStack Router’s useNavigate() takes typed route objects: navigate({ to: '/users/$id', params: { id: '123' } }) with autocomplete.

  4. Creates a flat route structure. Claude puts all routes at the same level. TanStack Router uses nested route trees with parent layouts containing <Outlet /> for child routes, and file-based routing generates this tree from directory structure.

The CLAUDE.md Configuration


# TanStack Router Project

## Routing
- Router: TanStack Router (@tanstack/react-router)
- File-based: routes in src/routes/ directory
- Code gen: @tanstack/router-plugin for route tree generation
- Search params: validated with Zod schemas

## TanStack Router Rules
- Routes defined with createFileRoute('/path') in route files
- File structure = route structure (src/routes/users/$id.tsx)
- Search params typed via validateSearch with Zod schema
- Navigation: navigate({ to: '/path', params: {}, search: {} })
- Link component: <Link to="/users/$id" params={{ id }} />
- Loaders: route.loader for data fetching before render
- Layout routes: __root.tsx, _layout.tsx files
- Route tree auto-generated — never edit routeTree.gen.ts

## Conventions
- Root layout: src/routes/__root.tsx
- Index route: src/routes/index.tsx
- Dynamic params: $paramName in filename ($id.tsx)
- Layout groups: _layout/ directory for shared layouts
- Pending UI: route.pendingComponent for loading states
- Error handling: route.errorComponent per route
- Search params schemas colocated in route files

Workflow Example

You want to add a user detail page with search params for tab selection. Prompt Claude Code:

“Create a /users/$id route with TanStack Router. Add a validated search param for the active tab (profile, settings, billing) with a default of profile. Include a loader that fetches user data, and show a pending component during loading.”

Claude Code should create src/routes/users/$id.tsx using createFileRoute('/users/$id') with validateSearch using a Zod schema for { tab: z.enum(['profile', 'settings', 'billing']).default('profile') }, a loader that fetches user data with the $id param, a pendingComponent for loading state, and the main component accessing typed search params via useSearch().

Common Pitfalls

  1. Editing the generated route tree. Claude modifies routeTree.gen.ts directly. This file is auto-generated by the TanStack Router plugin — any changes are overwritten. Create routes by adding files to src/routes/, not by editing the generated tree.

  2. Missing the router plugin in build config. Claude creates route files but does not configure @tanstack/router-plugin in the Vite or Webpack config. Without the plugin, the route tree is not generated and routes are not discovered.

  3. Using useParams without the route context. Claude calls useParams() in components that are not rendered inside the correct route. TanStack Router’s useParams() requires the component to be a child of the specific route — use Route.useParams() for the route-specific typed version.