TIP
This page mirrors .agents/tanstack-patterns.md from the repository. Edit it at the source, not here.
TanStack Patterns
Use this when working with TanStack Start routes, route structure, beforeLoad, and route-level React Query preloading.
This guide is intentionally focused on TanStack Router and TanStack Start. For other concerns, use the more specific docs:
Docs Lookup
Use vp run tanstack ... --json for TanStack documentation lookup.
vp run tanstack libraries --json
vp run tanstack doc router framework/react/guide/data-loading --json
vp run tanstack doc query framework/react/overview --docs-version v5 --json
vp run tanstack search-docs "server functions" --library start --json
vp run tanstack search-docs "loaders" --library router --framework react --jsonRouting
The web app uses TanStack Start file-based routing. In this codebase, routes/ acts as the FSD app layer.
FSD Layers
| Layer | Dir | Purpose |
|---|---|---|
| app | routes/ | Thin wrappers: loaders, guards, component references |
| pages | pages/ | Full page UI |
| widgets | widgets/ | Composite UI (layouts) |
| features | features/ | User-facing capabilities (forms, navbar) |
| shared | shared/ | Utilities, providers, hooks — no business logic |
Imports only go downward.
Segment Structure
segment-name/
index.ts → Barrel (public API, only re-exports)
ui/ → React components
lib/ → Business logic, integrations
utils/ → Pure helpers
types/ → Zod schemas + inferred types
hooks/ → Custom React hooks
stores/ → Zustand stores
api/ → Slice-local TanStack Query wrappers
get-thing.query.ts
create-thing.mutation.tsRoute Hierarchy
{-$locale}/— i18n locale prefix (optional param, base locale omits it)(root-layout)/— Navbar + Footer wrapper(centered-layout)/— Centered content wrapper(auth)/— Protected routes (auto-redirects to sign-in)(guest)/— Guest-only routes (auto-redirects authenticated users)
Adding a Route
- Create page component in
pages/<name>/ui/<name>-page.tsx, export viapages/<name>/index.ts - Create route in
routes/{-$locale}/(layout-group)/<name>/index.tsx - Route file imports the page component and wires
beforeLoadwith React Query preloading - Keep route files thin: route metadata, guards, preloading, and component wiring only
Route Files Stay Thin
- Put page UI in
pages/ - Put business logic in slice modules
- Put query and mutation wiring in slice-local
api/files - When route params or search state depend on shared enums or defaults, import the schema and defaults from
packages/coreinstead of recreatingz.enum([...])arrays in the route file - Avoid business logic directly in route files
Typical route responsibilities:
- auth or guest redirects
ensureQueryData(...)- not-found handling
- passing preloaded data into route context
React Query Preloading
All caching is via React Query, not the router loader cache. Router preload uses defaultPreloadStaleTime: 60_000 so hover intent does not re-run preloadRoute on every pass; React Query staleTime still governs data freshness.
Use ensureQueryData(...) in beforeLoad:
await context.queryClient.ensureQueryData(getThingQueryOptions(id));Use exported query option factories from slice modules. Do not inline query wiring in routes.
Gotchas
- Router preload stale time is 60s; React Query owns data freshness
- Caching lives in React Query, not the router cache
- Route files should stay thin
- Barrel files (
index.ts) are the public API - Pathless layout routes (parenthesized dirs) group routes without changing URLs