React + TanStack Query
Server state with TanStack Query: caching, mutations, optimistic updates.
# CLAUDE.md — React + TanStack Query
## Setup
- One `QueryClient` per app, instantiated above `<QueryClientProvider>`.
- Add `<ReactQueryDevtools>` in dev only — gate with `import.meta.env.DEV` or `process.env.NODE_ENV`.
- Default options live on the `QueryClient` constructor: `staleTime`, `retry`, `refetchOnWindowFocus`. Don't repeat them per-hook unless overriding.
## Query keys
- Keys are arrays. The first element is a stable string namespace; following elements are scoping params:
- `['users']` — the list
- `['users', userId]` — one user
- `['users', userId, 'posts']` — nested resource
- Define key factories per feature in `features/<feature>/queries.ts`:
```ts
export const userKeys = {
all: ['users'] as const,
detail: (id: string) => [...userKeys.all, id] as const,
}
```
- Never construct keys inline at call sites — invalidation will silently miss.
## Queries
- One custom hook per query: `useUser(id)`, `useUserList()`. The hook owns the query function and key.
- Always type the data via the query function's return type — don't type at the hook signature.
- Suspense queries (`useSuspenseQuery`) for data that's required to render the page; standard `useQuery` for optional data.
- `select` to derive computed values without re-running the query.
## Mutations
- One hook per mutation: `useUpdateUser()`. The hook owns the mutation function and post-mutation invalidation.
- After a mutation, invalidate the query keys that depend on the changed data — don't refetch a single specific query when a key prefix would do.
- For optimistic updates, use `onMutate` with `setQueryData` and roll back in `onError` using the snapshot.
## Caching
- `staleTime` defaults to 0 — set per-feature to a value that makes sense (5 minutes for user-list, 0 for live data).
- `gcTime` (formerly `cacheTime`) defaults to 5 minutes — increase for data the user revisits.
- `refetchOnWindowFocus`: `false` for dashboards where freshness is < 5 min anyway; `true` for actively editable resources.
## Don't
- Don't fetch in `useEffect` when TanStack Query exists. The cache, retry, and dedup logic are the point.
- Don't share query keys across features. Namespace strictly.
- Don't use a query for data that never changes — that's a constant. Import it directly.
- Don't disable a query with `enabled: false` and then refetch with `refetch()` — pass the right deps to the key instead.
- Don't put `useQuery` inside a conditional. The hook order is the cache identity.
Other React templates
Next.js App Router + TypeScript Rules
Server Components, Server Actions, and TypeScript discipline for the Next.js App Router.
Next.js + Server Actions + Shadcn UI
Forms with Server Actions, Zod validation, and Shadcn UI primitives.
React + TypeScript + Vite SPA Rules
Modern React SPA conventions: hooks, Suspense, error boundaries, Vite tuning.
React Hooks Best Practices
Disciplined hook usage: deps arrays, custom hooks, refs, and effect minimalism.
React + Redux Toolkit
Modern Redux with RTK, slices, and RTK Query for data fetching.