State Management
Keep server state, client state, and form state responsibilities explicit.
Recommended approach
Use different tools for different state classes instead of one global pattern.
- Server state: TanStack Query (fetching, caching, invalidation, background refresh).
- Local UI/app state: Zustand for shared client state with clear ownership.
- Form state: keep form-local state in form libraries/components unless globally required.
Decision rules
- If data comes from backend and needs caching or refetching, treat it as server state.
- If state coordinates UI across multiple components/routes, treat it as client app state.
- If state is short-lived and tied to a single form, keep it local.
Implementation checklist
- Define state ownership per module (
server,client,form). - Keep query keys and API contracts centralized in
api/modules. - Avoid duplicating server state into Zustand stores unless there is a clear reason.
- Add explicit invalidation rules for mutations.
- Keep derived UI state close to the consuming module.
Common pitfalls
- Storing remote server data in client stores by default.
- Creating global stores for one-screen interactions.
- Mixing async fetching concerns into presentational components.