Web Development5 min read

Zustand State Management

Manage global React state with Zustand - simple stores, actions, selectors, middleware, devtools, and persistence without boilerplate.

ZustandState ManagementReactFluxJavaScript

skill: Zustand State Management category: Web Development tags: [Zustand, State Management, React, Flux, JavaScript] readingTime: 5

Zustand is the sweet spot between useState (too local) and Redux (too much ceremony) for global React state. Its API is minimal - one hook, one store, no providers. Use it for UI state shared across the app; use React Query for server state.

Workflow

  1. Create a simple storeZustand stores are plain JavaScript objects with state and actions - no reducers, dispatchers, or action creators.
  2. Subscribe to specific slicesComponents that subscribe to the entire store re-render on any change. Selectors scope the subscription to only the relevant slice.
  3. Add devtools and persist middlewareDevtools integrates with Redux DevTools extension for time-travel debugging. Persist saves store to localStorage across reloads.
  4. Split large stores into slicesA single giant store becomes hard to maintain. Slices are independent pieces composed into one store.
  5. Update nested state immutablyZustand requires immutable updates - mutating state directly won't trigger re-renders. Use Immer middleware for deep nested updates.

Output Format

Zustand store: typed state + actions, selectors for performance, devtools + persist middleware, slices for modularity.

const useStore = create<State>()(devtools(persist(
  (set, get) => ({
    state: initialValue,
    action: (arg) => set(prev => ({ ...prev, field: newValue })),
  }),
  { name: 'store-key' }
)));

Examples

Input

Output

Edge Cases & Gotchas

  • Hydration mismatch with persist - persist causes SSR hydration mismatches (server state vs. localStorage). Use the skipHydration option and manually rehydrate on mount.
  • Stale actions in callbacks - Actions from useStore() are stable - they don't change between renders. You can safely include them in useEffect dependency arrays.
  • Computed values - Zustand has no built-in computed/derived values. Compute them in selectors: const count = useStore(s => s.items.length); or use a library like zustand-computed.
  • Cross-store access - Stores can't directly access each other's state via hooks (can't call hooks outside components). Use getState() from the other store: useOtherStore.getState().value.

When to Break These Rules

  • Use Jotai for atomic state (fine-grained per-atom subscriptions) when you have many independent pieces of state that need to be composed.
  • Use Redux Toolkit for large enterprise apps with time-travel debugging requirements, complex middleware (sagas), or team standardization needs.

Reference Files

  • https://zustand.docs.pmnd.rs/
  • https://github.com/pmndrs/zustand

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.