zustand-state-management
Build type-safe global state in React applications with Zustand. Supports TypeScript, persist middleware, devtools, slices pattern, and Next.js SSR. Use when setting up React state, migrating from Redux/Context API, implementing localStorage persistence, or troubleshooting Next.js hydration errors, TypeScript inference issues, or infinite render loops.
What this skill does
# Zustand State Management **Status**: Production Ready ✅ **Last Updated**: 2025-10-24 **Latest Version**: [email protected] **Dependencies**: React 18+, TypeScript 5+ --- ## Quick Start (3 Minutes) ### 1. Install Zustand ```bash npm install zustand # or pnpm add 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<BearStore>()((set) => ({ bears: 0, increase: (by) => set((state) => ({ bears: state.bears + by })), reset: () => set({ bears: 0 }), })) ``` **CRITICAL**: Notice the **double parentheses** `create<T>()()` - 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 <h1>{bears} around here...</h1> } function Controls() { const increase = useBearStore((state) => state.increase) return <button onClick={() => increase(1)}>Add bear</button> } ``` **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<CounterStore>()((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<T>()()` 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<UserPreferences>()( 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<T>()()` (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<T>(...)` (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 This skill prevents **5** documented issues: ### Issue #1: Next.js Hydration Mismatch **Error**: `"Text content does not match server-rendered HTML"` or `"Hydration failed"` **Source**: - [DEV Community: Persist middleware in Next.js](https://dev.to/abdulsamad/how-to-use-zustands-persist-middleware-in-nextjs-4lb5) - GitHub Discussions #2839 **Why It Happens**: Persist middleware reads from localStorage on client but not on server, causing state mismatch. **Prevention**: ```typescript import { create } from 'zustand' import { persist } from 'zustand/middleware' interface StoreWithHydration { count: number _hasHydrated: boolean setHasHydrated: (hydrated: boolean) => void increase: () => void } const useStore = create<StoreWithHydration>()( persist( (set) => ({ count: 0, _hasHydrated: false, setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }), increase: () => set((state) => ({ count: state.count + 1 })), }), { name: 'my-store', onRehydrateStorage: () => (state) => { state?.setHasHydrated(true) }, }, ), ) // In component function MyComponent() { const hasHydrated = useStore((state) => state._hasHydrated) if (!hasHydrated) { return <div>Loading...</div> } // Now safe to render with persisted state return <ActualContent /> } ``` ### Issue #2: TypeScript Double Parentheses Missing **Error**: Type inference fails, `StateCreator` types break with middleware **Source**: [Official Zustand TypeScript Guide](https://zustand.docs.pmnd.rs/guides/typescript) **Why It Happens**: The currying syntax `create<T>()()` is required for middleware to work with TypeScript inference. **Prevention**: ```typescript // ❌ WRONG - Single parentheses const useStore = create<MyStore>((set) => ({ // ... })) // ✅ CORRECT - Double parentheses const useStore = create<MyStore>()((set) => ({ // ... })) ``` **Rule**: Always use `create<T>()()` in TypeScript, even without middleware (future-proof). ### Issue #3: Persist Middleware Import Error **Error**: `"Attempted import error: 'createJSONStorage' is not exported from 'zustand/middleware'"` **Source**: GitHub Discussion #2839 **Why It Happens**: Wrong import path or version mismatch between zustand and build tools. **Prevention**: ```typescript // ✅ CORRECT imports for v5 import { create } from 'zustand' import { persist, createJSONStorage } from 'zustand/middleware' // Verify versions // [email protected] includes createJSONStorage // [email protected] uses different API // Check your package.json // "zustand": "^5.0.8" ``` ### Issue #4: Infinite Render Loop **Error**: Component re-renders infinitely, browser freezes **Source**: GitHub Discussions #2642 **Why It Happens**: Creating new object references in selectors causes Zustand to think state changed. **Prevention**: ```typescript import { shallow } from 'zustand/shallow' // ❌ WRONG - Creates new object every time const { bears, fishes } = useStore((state) => ({ bears: state.bears, fishes: state.fishes, })) // ✅ CORRECT Option 1 - Select primitives separately const bears = useStore((state) => state.bears) const fishes = useStore((state) => state.fishes) // ✅ CORRECT Option 2 - Use shallow for multiple values const { bears,
Related in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.