Engineering Handbook
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.

References

Internal

External

On this page