tanstack router react
via PatrickJS/awesome-cursorrules
Type-safe file-based routing for React with TanStack Router v1, featuring typed params, search validation, loaders, and auth guards.
What is tanstack router react?
TanStack Router provides 100% type-safe client-side routing for React with file-based route organization, typed route parameters and search params via Zod schemas, and data loading via loaders. Use this rule when building modern React applications that need scalable routing with TypeScript type safety, server-like data fetching patterns, and authentication guards.
- Define type-safe routes using file-based routing with createFileRoute and auto-generated route trees
- Validate and type search parameters using Zod schemas with validateSearch option
- Load data before render using loader functions integrated with TanStack Query for caching
- Implement authentication guards and redirects with beforeLoad hooks at the routing layer
- Navigate programmatically and declaratively with type-checked params and search using Link and useNavigate
- Handle errors, loading states, and 404s with errorComponent, pendingComponent, and notFoundComponent
Applies to
File patterns this rule matches.
Rule definition (reference)
Source of truth, from the repository.
You are an expert in TanStack Router, React, TypeScript, and modern type-safe client-side routing.
TanStack Router + React Guidelines
Core Philosophy
- TanStack Router is 100% type-safe — leverage TypeScript generics for route params, search params, and loader data
- Prefer file-based routing for scalability; use code-based routing only for highly dynamic use cases
- Always define routes with
createFileRouteorcreateRootRoute— never use plain objects - Route data loading belongs in
loaderfunctions, not in componentuseEffect - Search params are first-class citizens — define their schema with Zod or Valibot for validation and type inference
Project Setup
- Use
@tanstack/react-routerwith Vite and the@tanstack/router-vite-pluginfor file-based routing - Enable
routeTree.gen.tsauto-generation — never manually edit this file - Structure routes under
src/routes/directory - Root layout goes in
src/routes/__root.tsx - Use
src/routes/index.tsxfor the home/index route
File-Based Route Conventions
src/routes/
__root.tsx ← Root layout (wraps all routes)
index.tsx ← / route
about.tsx ← /about route
posts/
index.tsx ← /posts route
$postId.tsx ← /posts/:postId (dynamic segment)
_layout.tsx ← Layout route (no path segment)
_auth/
login.tsx ← /login (grouped under auth layout)
(admin)/
dashboard.tsx ← /dashboard (pathless group)
Route Definition Patterns
// src/routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => {
return fetchPost(params.postId) // fully typed params
},
component: PostComponent,
})
function PostComponent() {
const post = Route.useLoaderData() // type-safe loader data
const { postId } = Route.useParams() // type-safe params
return <div>{post.title}</div>
}
Type-Safe Search Params
- Always define search param schemas using
z.object()from Zod - Use
validateSearchoption on route definition - Access with
Route.useSearch()— never read rawwindow.location.search
import { z } from 'zod'
import { createFileRoute } from '@tanstack/react-router'
const searchSchema = z.object({
page: z.number().int().min(1).default(1),
q: z.string().optional(),
})
export const Route = createFileRoute('/search')({
validateSearch: searchSchema,
component: SearchPage,
})
function SearchPage() {
const { page, q } = Route.useSearch()
// ...
}
Navigation
- Use
<Link>from@tanstack/react-router— never<a href>for internal navigation - Use
useNavigate()for programmatic navigation - Always pass typed
paramsandsearchto Link — the compiler will catch mistakes
import { Link, useNavigate } from '@tanstack/react-router'
// Declarative
<Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>
// Programmatic
const navigate = useNavigate()
navigate({ to: '/posts/$postId', params: { postId: post.id } })
Loaders & Data Fetching
- Use
loaderfor data that must be available before render (no loading spinners for critical data) - Integrate with TanStack Query by using
ensureQueryDatainside loaders for caching - Use
staleTimeon loaders to avoid redundant fetches during navigation - Return plain serializable data from loaders — no class instances
export const Route = createFileRoute('/posts')({
loader: ({ context: { queryClient } }) =>
queryClient.ensureQueryData(postsQueryOptions()),
component: PostsPage,
})
Error Handling
- Define
errorComponenton routes to handle loader or render errors - Use
notFoundComponentfor 404 states within a route subtree - Use
pendingComponentfor showing skeletons/spinners during data loading
export const Route = createFileRoute('/posts/$postId')({
loader: fetchPost,
errorComponent: ({ error }) => <ErrorBanner message={error.message} />,
pendingComponent: () => <PostSkeleton />,
notFoundComponent: () => <NotFound />,
component: PostDetail,
})
Router Context
- Use router context to inject global dependencies (queryClient, auth, theme) into loaders
- Define context type in
__root.tsxand pass it when creating the router
// __root.tsx
import { createRootRouteWithContext } from '@tanstack/react-router'
interface RouterContext {
queryClient: QueryClient
auth: AuthState
}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootLayout,
})
// main.tsx
const router = createRouter({
routeTree,
context: { queryClient, auth },
})
Route Guards / Auth
- Use
beforeLoadfor authentication checks — redirect to login if unauthenticated - Never put auth logic inside components — handle it at the routing layer
export const Route = createFileRoute('/_auth/dashboard')({
beforeLoad: ({ context }) => {
if (!context.auth.isAuthenticated) {
throw redirect({ to: '/login' })
}
},
component: Dashboard,
})
Performance
- Use
preloadon<Link>to trigger loader prefetching on hover/focus - Set
defaultPreload: 'intent'on the router for automatic preloading - Use
gcTimeandstaleTimeon loaders to tune cache behavior - Lazy-load route components with
React.lazyfor code splitting
DevTools
- Install
@tanstack/router-devtoolsand render<TanStackRouterDevtools />in development - Use devtools to inspect route tree, active matches, loader data, and search params
Testing
- Use
createMemoryHistoryandcreateRouterto create isolated router instances in tests - Wrap components under test with
<RouterProvider router={testRouter} /> - Mock loaders by providing fake context values
Related rules
Senior full-stack TypeScript, React, Node.js guidance with clean architecture, testing, and WHY-oriented reasoning.
Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.
Android development with Jetpack Compose, clean architecture, and Material Design 3.
Angular development with Novo Elements UI library using standalone components.
Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.
Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.