Home › TanStack Start / Router › TanStack Start + Router Cursor rules
TanStack Start + Router Cursor rules
Cursor rules for TanStack Start apps: file-based TanStack Router, type-safe loaders, createServerFn, search params and TanStack Query.
CursorTanStack Start / Router
How to use
Save this as .cursor/rules/tanstack-start.mdc in your repository root, commit it, and your agent will pick it up automatically. Edit the commands to match your project.
Note: Review and test before use. Provided as is, without warranty. See Terms.
.cursor/rules/tanstack-start.mdcDownload ---
description: TanStack Start and TanStack Router conventions
globs: ["src/**/*.{ts,tsx}", "app/**/*.{ts,tsx}", "vite.config.ts"]
alwaysApply: false
---
# TanStack Start / TanStack Router
- Check `package.json` for `@tanstack/react-start` and `@tanstack/react-router` versions and follow that version's docs; Start moved fast before 1.0, so don't copy old `app.config.ts`/Vinxi examples into a Vite-plugin project.
- Routes live in `src/routes/` and are file-based. Each file exports `Route = createFileRoute('/path')({...})`. Never edit the generated `routeTree.gen.ts` by hand; the router plugin rewrites it.
- Root layout in `src/routes/__root.tsx` via `createRootRoute`/`createRootRouteWithContext<{ queryClient: QueryClient }>()`. Put shared context (query client, auth) there, not in globals.
- Data: load in the route `loader` (runs on server and client). With TanStack Query, call `context.queryClient.ensureQueryData(opts)` in the loader and `useSuspenseQuery(opts)` in the component, sharing one `queryOptions()` object.
- Server-only work goes in `createServerFn({ method: 'GET' | 'POST' })` with `.validator()` (zod) and `.handler()`. Secrets, DB clients and `process.env` reads stay inside server functions or files only they import.
- Search params are typed: `validateSearch` with a zod schema; read with `Route.useSearch()`, update with `navigate({ search: (prev) => ({ ...prev, page: 2 }) })`. Don't parse `window.location`.
- Links use `<Link to="/posts/$postId" params={{ postId }}>`; never string-concatenate paths. Let TypeScript catch bad routes.
- `beforeLoad` for auth guards; `throw redirect({ to: '/login' })`. `notFoundComponent` and `errorComponent` per route where it matters.
- Mutations: call a POST server function, then `router.invalidate()` or `queryClient.invalidateQueries()`.
## Don't
- Don't fetch in `useEffect` when a loader can do it, or import server-only modules from route components.
## Done when
`npm run build` and `npx tsc --noEmit` pass and the dev server regenerates the route tree without errors.