--- id: secondsky/claude-skills/zustand-state-management version: "2f1bb32a" license: MIT install: manual updated: 2026-07-25 --- # zustand-state-management — Zustand State Management teaches you to build type-safe global state for React without boilerplate. Master the three core patterns—basic stores, TypeScript-first setup, and persistent storage—while avoiding common pitfalls like hydration mismatches and infinite render loops. Publisher: secondsky · Stars: 196 · Updated: 2026-07-25 Install (manual): `git clone https://github.com/secondsky/claude-skills` ## SKILL.md # Zustand State Management **Status**: Production Ready ✅ **Last Updated**: 2025-11-21 **Latest Version**: zustand@5.0.8 **Dependencies**: React 18+, TypeScript 5+ --- ## Quick Start (3 Minutes) ### 1. Install Zustand ```bash bun add zustand # preferred # or: npm install zustand # or: yarn add zustand ``` **Why Zustand?** - Minimal API: Only 1 function to learn (`create`) - No boilerplate: No providers, reducers, or actions - TypeScript-first: Excellent type inference - Fast: Fine-grained subscriptions prevent unnecessary re-renders - Flexible: Middleware for persistence, devtools, and more ### 2. Create Your First Store (TypeScript) ```typescript import { create } from 'zustand' interface BearStore { bears: number increase: (by: number) => void reset: () => void } const useBearStore = create()((set) => ({ bears: 0, increase: (by) => set((state) => ({ bears: state.bears + by })), reset: () => set({ bears: 0 }), })) ``` **CRITICAL**: Notice the **double parentheses** `create()()` - this is required for TypeScript with middleware. ### 3. Use Store in Components ```tsx import { useBearStore } from './store' function BearCounter() { const bears = useBearStore((state) => state.bears) return

{bears} around here...

} function Controls() { const increase = useBearStore((state) => state.increase) return } ``` **Why this works:** - Components only re-render when their selected state changes - No Context providers needed - Selector function extracts specific state slice --- ## The 3-Pattern Setup Process ### Pattern 1: Basic Store (JavaScript) For simple use cases without TypeScript: ```javascript import { create } from 'zustand' const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) ``` **When to use:** - Prototyping - Small apps - No TypeScript in project ### Pattern 2: TypeScript Store (Recommended) For production apps with type safety: ```typescript import { create } from 'zustand' // Define store interface interface CounterStore { count: number increment: () => void decrement: () => void } // Create typed store const useCounterStore = create()((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) ``` **Key Points:** - Separate interface for state + actions - Use `create()()` syntax (currying for middleware) - Full IDE autocomplete and type checking ### Pattern 3: Persistent Store For state that survives page reloads: ```typescript import { create } from 'zustand' import { persist, createJSONStorage } from 'zustand/middleware' interface UserPreferences { theme: 'light' | 'dark' | 'system' language: string setTheme: (theme: UserPreferences['theme']) => void setLanguage: (language: string) => void } const usePreferencesStore = create()( persist( (set) => ({ theme: 'system', language: 'en', setTheme: (theme) => set({ theme }), setLanguage: (language) => set({ language }), }), { name: 'user-preferences', // unique name in localStorage storage: createJSONStorage(() => localStorage), // optional: defaults to localStorage }, ), ) ``` **Why this matters:** - State automatically saved to localStorage - Restored on page reload - Works with sessionStorage too - Handles serialization automatically --- ## Critical Rules ### Always Do ✅ Use `create()()` (double parentheses) in TypeScript for middleware compatibility ✅ Define separate interfaces for state and actions ✅ Use selector functions to extract specific state slices ✅ Use `set` with updater functions for derived state: `set((state) => ({ count: state.count + 1 }))` ✅ Use unique names for persist middleware storage keys ✅ Handle Next.js hydration with `hasHydrated` flag pattern ✅ Use `shallow` for selecting multiple values ✅ Keep actions pure (no side effects except state updates) ### Never Do ❌ Use `create(...)` (single parentheses) in TypeScript - breaks middleware types ❌ Mutate state directly: `set((state) => { state.count++; return state })` - use immutable updates ❌ Create new objects in selectors: `useStore((state) => ({ a: state.a }))` - causes infinite renders ❌ Use same storage name for multiple stores - causes data collisions ❌ Access localStorage during SSR without hydration check ❌ Use Zustand for server state - use TanStack Query instead ❌ Export store instance directly - always export the hook --- ## Known Issues Prevention (5 Issues) | Issue | Error | Quick Fix | |-------|-------|-----------| | **#1 Hydration mismatch** | "Text content does not match" | Use `_hasHydrated` flag + `onRehydrateStorage` | | **#2 TypeScript inference** | Types break with middleware | Use `create()()` double parentheses | | **#3 Import error** | "createJSONStorage not exported" | Upgrade to zustand@5.0.8+ | | **#4 Infinite loop** | Browser freezes | Use `shallow` or separate selectors | | **#5 Slices types** | StateCreator types fail | Explicit `StateCreator` | **Most Critical** - TypeScript double parentheses: ```typescript // ❌ WRONG: create((set) => ...) // ✅ CORRECT: create()((set) => ...) ``` **See**: `references/known-issues.md` for complete solutions with code examples. --- ## Middleware Configuration ```typescript import { create } from 'zustand' import { devtools, persist } from 'zustand/middleware' const useStore = create()( devtools( persist( (set) => ({ /* store definition */ }), { name: 'my-storage' }, ), { name: 'MyStore' }, ), ) ``` | Middleware | Purpose | Import | |------------|---------|--------| | `persist` | localStorage/sessionStorage | `zustand/middleware` | | `devtools` | Redux DevTools integration | `zustand/middleware` | | `immer` | Mutable update syntax | `zustand/middleware/immer` | **Order matters**: `devtools(persist(...))` shows persist actions in DevTools. **See**: `references/middleware-guide.md` for complete middleware documentation. --- ## Common Patterns | Pattern | Use Case | Key Technique | |---------|----------|---------------| | **Computed values** | Derived data | Compute in selector: `state.items.length` | | **Async actions** | API calls | `set({ isLoading: true })` + try/catch | | **Reset store** | Logout, form clear | `set(initialState)` | | **Selector with params** | Dynamic access | `state.todos.find(t => t.id === id)` | | **Multiple stores** | Separation of concerns | Create separate `create()` calls | **See**: `references/common-patterns.md` for complete implementations. --- ## Advanced Topics | Topic | Use Case | Key API | |-------|----------|---------| | **Vanilla store** | Non-React, testing | `createStore()` from `zustand/vanilla` | | **Custom middleware** | Logging, timestamps | Wrap `StateCreator` | | **Immer** | Mutable update syntax | `immer()` middleware | | **Subscriptions** | Side effects | `store.subscribe()` | **See**: `references/advanced-topics.md` for complete implementations. --- ## Bundled Resources | Type | Files | |------|-------| | **Templates** | `basic-store.ts`, `typescript-store.ts`, `persist-store.ts`, `slices-pattern.ts`, `devtools-store.ts`, `nextjs-store.ts`, `computed-store.ts`, `async-actions-store.ts` | | **References** | `middleware-guide.md`, `typescript-patterns.md`, `nextjs-hydration.md`, `migration-guide.md`, `known-issues.md`, `common-patterns.md`, `advanced-topics.md` | --- ## When to Load References | Reference | Load When... | |-----------|--------------| | `known-issues.md` | Debugging hydration, TypeScript, infinite loop, or slices errors | | `common-patterns.md` | Implementing computed values, async actions, reset patterns | | `advanced-topics.md` | Vanilla stores, custom middleware, Immer, subscriptions | | `middleware-guide.md` | Configuring persist, devtools, or combining middlewares | | `typescript-patterns.md` | Complex type inference issues, StateCreator problems | | `nextjs-hydration.md` | Next.js SSR/hydration problems | | `migration-guide.md` | Migrating from Redux, Context API, or Zustand v4 | --- ## Quick Troubleshooting | Problem | Solution | |---------|----------| | Store updates don't trigger re-renders | Use selector: `useStore(state => state.value)` not destructuring | | TypeScript errors with middleware | Use `create()()` double parentheses | | Hydration error with persist | Implement `_hasHydrated` flag pattern | | Actions not showing in DevTools | Pass action name: `set(newState, undefined, 'actionName')` | | Store resets unexpectedly | HMR causes reset in development | --- ## Dependencies ```json { "dependencies": { "zustand": "^5.0.8", "react": "^18.0.0+" } } ``` **Compatibility**: React 18+, React 19, TypeScript 5+, Next.js 14+, Vite 5+ --- **Official Docs**: https://zustand.docs.pmnd.rs/ | **GitHub**: https://github.com/pmndrs/zustand [View on SkillFed](https://skillfed.io/secondsky/claude-skills/zustand-state-management) · [View on GitHub](https://github.com/secondsky/claude-skills)