State Management
Priority: P2 (MEDIUM)
Decision Guide
- Shareable/persistent? Use URL state (
useSearchParams+useRouter). - Server data? Use SWR or TanStack Query. Never sync into
useState. - Complex client UI? Use Zustand (in
'use client'only) or Jotai. - Simple local? Use
useState. Colocate as close to consumer as possible.
URL-Driven State
Server State (SWR / TanStack Query)
Client State (Zustand)
Hydration Safety
Wrap localStorage reads in useEffect or mounted flag to avoid hydration mismatches. Manage optimistic updates with useOptimistic in Next.js 15+.
Legacy Redux (existing projects)
If project already uses redux@4 + createStore + redux-thunk + next-redux-wrapper:
- Use
useSelector/useDispatchhooks — never connect HOC. - Define typed
RootStateand typedAppDispatchfor all selectors and dispatch calls. - Avoid adding Zustand or TanStack Query on top of existing Redux codebase — migrate incrementally if needed.
- Migration path: Redux Toolkit (
@reduxjs/toolkit) → RTK Query → then consider TanStack Query.
See references/redux.md for typed selector and thunk patterns.
Library Patterns
Anti-Patterns
- No global store for simple state: Use
useStateor URL params; avoid Zustand for basic UI. - No large objects in state: Decompose into granular primitives to prevent extra re-renders.
- No
useEffectfor data fetching: Use SWR or TanStack Query for server state. - No server state in client stores: Fetch in RSCs; client stores for UI-only state.