PluginBench
Rule

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 createFileRoute or createRootRoute — never use plain objects
  • Route data loading belongs in loader functions, not in component useEffect
  • Search params are first-class citizens — define their schema with Zod or Valibot for validation and type inference

Project Setup

  • Use @tanstack/react-router with Vite and the @tanstack/router-vite-plugin for file-based routing
  • Enable routeTree.gen.ts auto-generation — never manually edit this file
  • Structure routes under src/routes/ directory
  • Root layout goes in src/routes/__root.tsx
  • Use src/routes/index.tsx for 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 validateSearch option on route definition
  • Access with Route.useSearch() — never read raw window.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 params and search to 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 loader for data that must be available before render (no loading spinners for critical data)
  • Integrate with TanStack Query by using ensureQueryData inside loaders for caching
  • Use staleTime on 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 errorComponent on routes to handle loader or render errors
  • Use notFoundComponent for 404 states within a route subtree
  • Use pendingComponent for 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.tsx and 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 beforeLoad for 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 preload on <Link> to trigger loader prefetching on hover/focus
  • Set defaultPreload: 'intent' on the router for automatic preloading
  • Use gcTime and staleTime on loaders to tune cache behavior
  • Lazy-load route components with React.lazy for code splitting

DevTools

  • Install @tanstack/router-devtools and render <TanStackRouterDevtools /> in development
  • Use devtools to inspect route tree, active matches, loader data, and search params

Testing

  • Use createMemoryHistory and createRouter to 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.

**/*
41k
via PatrickJS/awesome-cursorrules

Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.

**/*
41k
via PatrickJS/awesome-cursorrules

Android development with Jetpack Compose, clean architecture, and Material Design 3.

**/*
41k
via PatrickJS/awesome-cursorrules

Angular development with Novo Elements UI library using standalone components.

**/*
41k
via PatrickJS/awesome-cursorrules

Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.

**/*
41k
via PatrickJS/awesome-cursorrules

Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.

**/*.sh +5
41k
via PatrickJS/awesome-cursorrules