{"filename":"tanstack-router-react-cursorrules-prompt-file.mdc","content":"---\ndescription: \"Cursor rules for TanStack Router v1 with file-based routing, typed params, search validation, loaders, auth guards, and route preloading.\"\nglobs: **/*\nalwaysApply: false\n---\nYou are an expert in TanStack Router, React, TypeScript, and modern type-safe client-side routing.\n\n# TanStack Router + React Guidelines\n\n## Core Philosophy\n- TanStack Router is 100% type-safe — leverage TypeScript generics for route params, search params, and loader data\n- Prefer file-based routing for scalability; use code-based routing only for highly dynamic use cases\n- Always define routes with `createFileRoute` or `createRootRoute` — never use plain objects\n- Route data loading belongs in `loader` functions, not in component `useEffect`\n- Search params are first-class citizens — define their schema with Zod or Valibot for validation and type inference\n\n## Project Setup\n- Use `@tanstack/react-router` with Vite and the `@tanstack/router-vite-plugin` for file-based routing\n- Enable `routeTree.gen.ts` auto-generation — never manually edit this file\n- Structure routes under `src/routes/` directory\n- Root layout goes in `src/routes/__root.tsx`\n- Use `src/routes/index.tsx` for the home/index route\n\n## File-Based Route Conventions\n```\nsrc/routes/\n  __root.tsx          ← Root layout (wraps all routes)\n  index.tsx           ← / route\n  about.tsx           ← /about route\n  posts/\n    index.tsx         ← /posts route\n    $postId.tsx       ← /posts/:postId (dynamic segment)\n    _layout.tsx       ← Layout route (no path segment)\n  _auth/\n    login.tsx         ← /login (grouped under auth layout)\n  (admin)/\n    dashboard.tsx     ← /dashboard (pathless group)\n```\n\n## Route Definition Patterns\n```tsx\n// src/routes/posts/$postId.tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/posts/$postId')({\n  loader: async ({ params }) => {\n    return fetchPost(params.postId) // fully typed params\n  },\n  component: PostComponent,\n})\n\nfunction PostComponent() {\n  const post = Route.useLoaderData() // type-safe loader data\n  const { postId } = Route.useParams() // type-safe params\n  return <div>{post.title}</div>\n}\n```\n\n## Type-Safe Search Params\n- Always define search param schemas using `z.object()` from Zod\n- Use `validateSearch` option on route definition\n- Access with `Route.useSearch()` — never read raw `window.location.search`\n```tsx\nimport { z } from 'zod'\nimport { createFileRoute } from '@tanstack/react-router'\n\nconst searchSchema = z.object({\n  page: z.number().int().min(1).default(1),\n  q: z.string().optional(),\n})\n\nexport const Route = createFileRoute('/search')({\n  validateSearch: searchSchema,\n  component: SearchPage,\n})\n\nfunction SearchPage() {\n  const { page, q } = Route.useSearch()\n  // ...\n}\n```\n\n## Navigation\n- Use `<Link>` from `@tanstack/react-router` — never `<a href>` for internal navigation\n- Use `useNavigate()` for programmatic navigation\n- Always pass typed `params` and `search` to Link — the compiler will catch mistakes\n```tsx\nimport { Link, useNavigate } from '@tanstack/react-router'\n\n// Declarative\n<Link to=\"/posts/$postId\" params={{ postId: '123' }}>View Post</Link>\n\n// Programmatic\nconst navigate = useNavigate()\nnavigate({ to: '/posts/$postId', params: { postId: post.id } })\n```\n\n## Loaders & Data Fetching\n- Use `loader` for data that must be available before render (no loading spinners for critical data)\n- Integrate with TanStack Query by using `ensureQueryData` inside loaders for caching\n- Use `staleTime` on loaders to avoid redundant fetches during navigation\n- Return plain serializable data from loaders — no class instances\n```tsx\nexport const Route = createFileRoute('/posts')({\n  loader: ({ context: { queryClient } }) =>\n    queryClient.ensureQueryData(postsQueryOptions()),\n  component: PostsPage,\n})\n```\n\n## Error Handling\n- Define `errorComponent` on routes to handle loader or render errors\n- Use `notFoundComponent` for 404 states within a route subtree\n- Use `pendingComponent` for showing skeletons/spinners during data loading\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n  loader: fetchPost,\n  errorComponent: ({ error }) => <ErrorBanner message={error.message} />,\n  pendingComponent: () => <PostSkeleton />,\n  notFoundComponent: () => <NotFound />,\n  component: PostDetail,\n})\n```\n\n## Router Context\n- Use router context to inject global dependencies (queryClient, auth, theme) into loaders\n- Define context type in `__root.tsx` and pass it when creating the router\n```tsx\n// __root.tsx\nimport { createRootRouteWithContext } from '@tanstack/react-router'\n\ninterface RouterContext {\n  queryClient: QueryClient\n  auth: AuthState\n}\n\nexport const Route = createRootRouteWithContext<RouterContext>()({\n  component: RootLayout,\n})\n\n// main.tsx\nconst router = createRouter({\n  routeTree,\n  context: { queryClient, auth },\n})\n```\n\n## Route Guards / Auth\n- Use `beforeLoad` for authentication checks — redirect to login if unauthenticated\n- Never put auth logic inside components — handle it at the routing layer\n```tsx\nexport const Route = createFileRoute('/_auth/dashboard')({\n  beforeLoad: ({ context }) => {\n    if (!context.auth.isAuthenticated) {\n      throw redirect({ to: '/login' })\n    }\n  },\n  component: Dashboard,\n})\n```\n\n## Performance\n- Use `preload` on `<Link>` to trigger loader prefetching on hover/focus\n- Set `defaultPreload: 'intent'` on the router for automatic preloading\n- Use `gcTime` and `staleTime` on loaders to tune cache behavior\n- Lazy-load route components with `React.lazy` for code splitting\n\n## DevTools\n- Install `@tanstack/router-devtools` and render `<TanStackRouterDevtools />` in development\n- Use devtools to inspect route tree, active matches, loader data, and search params\n\n## Testing\n- Use `createMemoryHistory` and `createRouter` to create isolated router instances in tests\n- Wrap components under test with `<RouterProvider router={testRouter} />`\n- Mock loaders by providing fake context values\n"}