Home › All rules › Community › Tanstack Router React Cursor Rules

Tanstack Router React Cursor Rules

Cursor rules for TanStack Router v1 with file-based routing, typed params, search validation, loaders, auth guards, and route preloading.

CursorReactCommunity (CC0)

How to use

Save this as .cursor/rules/tanstack-router-react-cursorrules-prompt-file.mdc in your project, commit it, and Cursor will load it. Review it first: community files can be opinionated or out of date.

Note: Review and test before use. Provided as is, without warranty. See Terms.

.cursor/rules/tanstack-router-react-cursorrules-prompt-file.mdcDownload  
---
description: "Cursor rules for TanStack Router v1 with file-based routing, typed params, search validation, loaders, auth guards, and route preloading."
globs: **/*
alwaysApply: false
---
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
```tsx
// 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`
```tsx
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
```tsx
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
```tsx
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
```tsx
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
```tsx
// __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
```tsx
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

More: React rules · all community rules

Related rules

React + TypeScript Copilot path instructions

Path-specific Copilot instructions for React 19 + TypeScript .tsx files: components, hooks, accessibility, and tests.

GitHub CopilotReact

React 19 + Vite SPA rules

Cursor rules for React 19 single-page apps on Vite 6+ with TypeScript, hooks, and Vitest.

CursorReact

TanStack Query v5 rules

Cursor rules for TanStack Query v5 in React: query keys, queryOptions, mutations, and cache invalidation.

CursorReact

Cursor AI React TypeScript Shadcn Ui Cursor Rules

Cursor rules for Cursor AI development with React, TypeScript, and shadcn/ui integration.

CursorReactCommunity (CC0)

React Chakra Ui Cursor Rules

Cursor rules for React development with Chakra UI integration.

CursorReactCommunity (CC0)

React Components Creation Cursor Rules

Cursor rules for React component creation and development.

CursorReactCommunity (CC0)

Get new rules weekly

One email a week: fresh rule files, agent tips, and what's changing in Cursor, Claude Code and Copilot. Free, unsubscribe anytime.

Opens the Rulestack Newsletter on beehiiv in a new tab. Or subscribe directly on beehiiv.