TanStack Router Patterns
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: tanstack-router for comprehensive documentation.
When NOT to Use This Skill
Skip this skill when:
-
Using React Router v6 (different API)
-
Building Next.js apps with App Router (use RSC patterns)
-
Working with Vue Router (use Vue-specific routing)
-
Using Angular Router (use Angular routing)
-
Client-side routing not needed (static sites)
File-Based Route Structure
src/routes/ ├── __root.tsx # Root layout ├── _authenticated.tsx # Auth guard layout ├── _authenticated/ │ ├── dashboard.tsx # /dashboard │ └── users/ │ ├── index.tsx # /users │ └── $userId.tsx # /users/:userId ├── login.tsx # /login └── register.tsx # /register
Root Route
// routes/__root.tsx import { createRootRoute, Outlet } from '@tanstack/react-router'; import { QueryClientProvider } from '@tanstack/react-query'; import { Toaster } from 'sonner'; import { queryClient } from '@/lib/query-client';
export const Route = createRootRoute({ component: RootComponent, });
function RootComponent() { return ( <QueryClientProvider client={queryClient}> <Outlet /> <Toaster richColors position="top-right" /> </QueryClientProvider> ); }
Auth Guard Layout
// routes/_authenticated.tsx import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'; import { useAuthStore } from '@/stores/authStore'; import { MainLayout } from '@/layouts/MainLayout';
export const Route = createFileRoute('/_authenticated')({ beforeLoad: ({ location }) => { const isAuthenticated = useAuthStore.getState().isAuthenticated; if (!isAuthenticated) { throw redirect({ to: '/login', search: { redirect: location.href }, }); } }, component: AuthenticatedLayout, });
function AuthenticatedLayout() { return ( <MainLayout> <Outlet /> </MainLayout> ); }
Protected Route with Loader
// routes/_authenticated/users/index.tsx import { createFileRoute } from '@tanstack/react-router'; import { usersApi } from '@/api/users.api'; import { UserList } from '@/features/users/components/UserList';
export const Route = createFileRoute('/_authenticated/users/')({ loader: async () => { return usersApi.getAll(); }, component: UsersPage, });
function UsersPage() { const users = Route.useLoaderData(); return <UserList users={users} />; }
Dynamic Route with Params
// routes/_authenticated/users/$userId.tsx import { createFileRoute, notFound } from '@tanstack/react-router'; import { usersApi } from '@/api/users.api'; import { UserDetail } from '@/features/users/components/UserDetail';
export const Route = createFileRoute('/_authenticated/users/$userId')({ loader: async ({ params }) => { const user = await usersApi.getById(params.userId); if (!user) throw notFound(); return user; }, component: UserDetailPage, notFoundComponent: () => <div>User not found</div>, });
function UserDetailPage() { const user = Route.useLoaderData(); return <UserDetail user={user} />; }
Navigation
import { Link, useNavigate, useRouter } from '@tanstack/react-router';
// Declarative navigation <Link to="/users/$userId" params={{ userId: '123' }}> View User </Link>
// Programmatic navigation const navigate = useNavigate(); navigate({ to: '/dashboard' }); navigate({ to: '/users/$userId', params: { userId: '123' } });
// With search params navigate({ to: '/users', search: { page: 1, sort: 'name' }, });
// Go back const router = useRouter(); router.history.back();
Search Params with Validation
import { createFileRoute } from '@tanstack/react-router'; import { z } from 'zod';
const searchSchema = z.object({ page: z.number().default(1), search: z.string().optional(), sort: z.enum(['name', 'email', 'createdAt']).default('createdAt'), });
export const Route = createFileRoute('/_authenticated/users/')({ validateSearch: searchSchema, component: UsersPage, });
function UsersPage() { const { page, search, sort } = Route.useSearch(); // Type-safe search params }
Router Configuration
// router.ts import { createRouter } from '@tanstack/react-router'; import { routeTree } from './routeTree.gen';
export const router = createRouter({ routeTree, defaultPreload: 'intent', defaultPreloadStaleTime: 0, });
declare module '@tanstack/react-router' { interface Register { router: typeof router; } }
Key Concepts
Concept Description
__root.tsx
Global layout, providers
_layout.tsx
Route group layout
$param.tsx
Dynamic route segment
index.tsx
Index route
beforeLoad
Pre-navigation guard
loader
Data fetching
notFound
404 handling
Anti-Patterns
Anti-Pattern Why It's Bad Correct Approach
Fetching in components Not type-safe, duplicate requests Use route loaders
Not using search param validation Runtime errors Use Zod schema validation
Hardcoded navigation paths Type-unsafe, breaks on refactor Use typed Link and navigate
Mixing React Router patterns Different API Use TanStack Router idioms
Not using beforeLoad for auth Auth check in component Use beforeLoad hook
Large route files Hard to maintain Split into feature modules
Quick Troubleshooting
Issue Likely Cause Solution
Type error on navigation Wrong route path Use autocomplete from to prop
Loader not running Missing loader in route Add loader to route definition
Redirect not working Wrong redirect syntax Use throw redirect({ to: '...' })
Search params not updating Not using useSearch
Use Route.useSearch()
Route not found File naming wrong Check file-based routing convention
Auth guard not firing Not in beforeLoad
Move auth check to beforeLoad
Reference Documentation
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: tanstack-router for comprehensive documentation.
-
File-based Routing
-
Auth Guards