{"filename":"tanstack-router.mdc","content":"---\ndescription: \"Type-safe routing with TanStack Router v1 for React apps, including file-based routing, loaders, search params validation, auth guards, and TanStack Query integration\"\nglobs: [\"src/routes/**/*\", \"src/routeTree.gen.ts\", \"app.config.ts\"]\nalwaysApply: false\n---\nYou are an expert in TanStack Router v1, React, TypeScript, and type-safe client-side routing.\n\n## Core Principles\n- TanStack Router is 100% type-safe — leverage TypeScript generics for params, search params, and loader data\n- Prefer file-based routing with `@tanstack/router-vite-plugin` for scalability\n- Always define routes with `createFileRoute` or `createRootRoute`\n- Route data loading belongs in `loader` functions, not in component `useEffect`\n- Search params are first-class — always define their schema with Zod for type safety\n\n## File-Based Route Conventions\n```\nsrc/routes/\n  __root.tsx          ← Root layout\n  index.tsx           ← / route\n  posts/\n    index.tsx         ← /posts\n    $postId.tsx       ← /posts/:postId (dynamic)\n    _layout.tsx       ← Layout route (no path segment)\n  _auth/              ← Pathless auth layout group\n    dashboard.tsx\n```\n\n## Route Definition\n```tsx\nexport const Route = createFileRoute('/posts/$postId')({\n  loader: async ({ params }) => fetchPost(params.postId),\n  component: PostComponent,\n  errorComponent: ({ error }) => <ErrorBanner message={error.message} />,\n  pendingComponent: () => <PostSkeleton />,\n})\n\nfunction PostComponent() {\n  const post = Route.useLoaderData()     // type-safe\n  const { postId } = Route.useParams()  // type-safe\n  return <div>{post.title}</div>\n}\n```\n\n## Type-Safe Search Params\n- Always define search params with Zod and `validateSearch`\n- Access with `Route.useSearch()` — never read `window.location.search` directly\n```tsx\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```\n\n## Navigation\n- Use `<Link>` for internal navigation — never `<a href>`\n- Always pass typed `params` and `search` — the compiler will catch mistakes\n```tsx\n<Link to=\"/posts/$postId\" params={{ postId: '123' }}>View Post</Link>\n```\n\n## Loaders + TanStack Query Integration\n```tsx\nexport const Route = createFileRoute('/posts')({\n  loader: ({ context: { queryClient } }) =>\n    queryClient.ensureQueryData(postsQueryOptions()),\n  component: PostsPage,\n})\n```\n\n## Router Context for Dependency Injection\n```tsx\n// __root.tsx\ninterface RouterContext { queryClient: QueryClient; auth: AuthState }\nexport const Route = createRootRouteWithContext<RouterContext>()({ component: RootLayout })\n\n// main.tsx\nconst router = createRouter({ routeTree, context: { queryClient, auth } })\n```\n\n## Auth Guards\n```tsx\nexport const Route = createFileRoute('/_auth/dashboard')({\n  beforeLoad: ({ context }) => {\n    if (!context.auth.isAuthenticated) throw redirect({ to: '/login' })\n  },\n  component: Dashboard,\n})\n```\n\n## Performance\n- Set `defaultPreload: 'intent'` on router for automatic prefetching on hover/focus\n- Use `React.lazy` for route component code splitting\n- Install `@tanstack/router-devtools` and render `<TanStackRouterDevtools />` in development\n"}