Skip to content

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.

sh
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 --json

Routing

The web app uses TanStack Start file-based routing. In this codebase, routes/ acts as the FSD app layer.

FSD Layers

LayerDirPurpose
approutes/Thin wrappers: loaders, guards, component references
pagespages/Full page UI
widgetswidgets/Composite UI (layouts)
featuresfeatures/User-facing capabilities (forms, navbar)
sharedshared/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.ts

Route 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

  1. Create page component in pages/<name>/ui/<name>-page.tsx, export via pages/<name>/index.ts
  2. Create route in routes/{-$locale}/(layout-group)/<name>/index.tsx
  3. Route file imports the page component and wires beforeLoad with React Query preloading
  4. 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/core instead of recreating z.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:

ts
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

Released under the MIT License.