Frontend
Zustand
Zustand best practices for shared client state in React applications.
Zustand
What it is
Zustand is a minimal state management library for React that uses hooks and small store definitions.
Best practices
Why we use it
- Keeps global client-state logic simple and explicit.
- Avoids boilerplate for UI/workflow states that cross component boundaries.
- Works well with React hooks and TypeScript inference.
Setup in this repo
- Define stores in shared state modules with one clear responsibility per store.
- Keep store shape stable and action names explicit.
- Export hook selectors for common read paths.
Team conventions
- Prefer local component state first; use Zustand only for shared client state.
- Keep async server-state in TanStack Query, not in Zustand.
- Expose actions with explicit names (
setFilter,resetState,toggleSidebar). - Prefer selector-based reads to avoid unnecessary rerenders.
Error handling and reliability
- Validate external payloads before writing to store.
- Keep default state values deterministic and serializable where possible.
- Provide reset actions for recoverability in complex workflows.
Testing and validation
- Unit test stores for default state and action transitions.
- Verify selectors and derived state behavior.
- Test integration points with UI for shared state flows.
Abstractions and anti-patterns
- Avoid large monolithic stores with unrelated concerns.
- Avoid writing server cache copies into Zustand.
- Avoid hidden state coupling across distant features.
Example
import { create } from "zustand";
type UiStore = {
sidebarOpen: boolean;
toggleSidebar: () => void;
};
export const useUiStore = create<UiStore>((set) => ({
sidebarOpen: false,
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
}));Common pitfalls
- Putting server data in Zustand instead of using TanStack Query cache.
- Creating very large stores with unrelated concerns.
- Updating entire store objects when only a small slice changes.
- Leaving actions untyped or ambiguously named.