/web-state-zustand
Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.
$ npx -y skills add agents-inc/skills --skill web-state-zustand --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/web-state-zustand
Context preview
The summary Claude sees to decide when to auto-load this skill.
Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.
SKILL.md
web-state-zustand.SKILL.mdname: web-state-zustand
description: Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.
Client State Management Patterns
> **Quick Guide:** Local UI state? useState. Shared UI (2+ components)? Zustand. Server data? Use your data fetching solution. URL-appropriate filters? searchParams. NEVER use Context for state management. Zustand v5: use `useShallow` from `zustand/react/shallow` (not the old equality-fn second arg), selectors must return stable references, and `persist` no longer stores initial state during creation.
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Store setup, selectors, useShallow, Context anti-patterns, URL state
---
<critical_requirements>
CRITICAL: Before Managing Client State
**(You MUST use a data fetching solution for ALL server/API data - NEVER useState, Zustand, or Context)**
**(You MUST use Zustand for ALL shared UI state (2+ components) - NOT Context or prop drilling)**
**(You MUST use useState ONLY for truly component-local state - NOT for anything shared)**
**(You MUST use atomic selectors or `useShallow` from `zustand/react/shallow` - NEVER destructure the entire store)**
**(You MUST ensure selectors return stable references - inline object/function creation causes infinite loops in v5)**
</critical_requirements>
---
**Auto-detection:** Zustand, zustand, create from zustand, useShallow, zustand/middleware, zustand store, client state, shared UI state, Context misuse, prop drilling, global state
**When to use:**
- Deciding between Zustand or useState for a use case
- Setting up Zustand for shared UI state (modals, sidebars, preferences)
- Understanding when NOT to use Context for state management
- Structuring stores: slices, actions, selectors
**Key patterns covered:**
- Client state = useState (local) or Zustand (shared, 2+ components)
- Context for dependency injection only (NEVER for state management)
- Store setup with devtools and persist middleware
- Selector patterns: atomic selectors vs useShallow
- URL params for shareable/bookmarkable state (filters, search)
**When NOT to use:**
- Server/API data (use a dedicated data fetching solution)
- State that should be shareable via URL (use searchParams)
- Any Context-based state management approach
---
<philosophy>
Philosophy
Zustand is a minimal, hook-based state manager. The key principle: **use the right tool for the right job**. Server data belongs in a dedicated data fetching layer with caching and synchronization. Local UI state stays in useState. Shared UI state lives in Zustand for performance. URL state makes filters shareable. Context is ONLY for dependency injection, never state management.
**Store design principles** (from TkDodo and official docs):
- **Keep stores small** - multiple focused stores beat one monolithic store
- **Business logic in the store** - components call actions, stores decide what happens
- **Only export custom hooks** - never expose the raw store creator
- **Atomic selectors preferred** - return single values, not objects, for best performance
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: State Placement Decision
The most critical decision: where does this state belong?
Is it server data (from API)?
├─ YES → Data fetching solution (not this skill's scope)
└─ NO → Is it URL-appropriate (filters, search)?
├─ YES → URL params (searchParams)
└─ NO → Is it needed in 2+ components?
├─ YES → Zustand
└─ NO → Is it truly component-local?
├─ YES → useState
└─ NO → Is it a singleton/dependency?
└─ YES → Context (ONLY for DI, not state)For full examples, see [examples/core.md](examples/core.md#pattern-1-state-placement).
---
Pattern 2: Local State with useState
Use ONLY when state is truly component-local and never shared.
- State used ONLY in one component (isExpanded, isOpen)
- Temporary UI state that never needs to be shared
- As soon as a second component needs it, move to Zustand
For good/bad comparisons, see [examples/core.md](examples/core.md#pattern-2-local-state-with-usestate).
---
Pattern 3: Zustand Store Setup
Use as soon as state is needed in 2+ components across the tree.
// stores/ui-store.ts
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
const UI_STORAGE_KEY = "ui-storage";
interface UIState {
sidebarOpen: boolean;
theme: "light" | "dark";
toggleSidebar: () => void;
setTheme: (theme: "light" | "dark") => void;
}
export const useUIStore = create<UIState>()(
devtools(
persist(
(set) => ({
sidebarOpen: true,
theme: "light",
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
setTheme: (theme) => set({ theme }),
}),
{ name: UI_STORAGE_KEY, partialize: (s) => ({ theme: s.theme }) },
),
),
);**Key points:** devtools for debugging, persist only what survives sessions (preferences, not transient UI), `partialize` to exclude ephemeral state.
For selectors, useShallow, and v5 stability patterns, see [examples/core.md](examples/core.md#pattern-3-zustand-store-setup).
---
Pattern 4: Context API - Dependency Injection ONLY
Context is NOT a state management solution. It's for dependency injection and singletons ONLY.
**ONLY use Context for:**
- Framework providers (router, query client)
- Dependency injection (services, API clients, DB connections)
- Values set once at app initialization that never change
**NEVER use Context for:**
- ANY state management (use Zustand instead)
- ANY frequently updating values (every consumer re-renders on any change)
For why Context fails for state and acceptable DI usage, see [examples/core.md](examples/core.md#pattern-4-context-api---dependency-injection-only).
---
Pattern 5: URL State for Shareable Filters
Use URL params
Read more
name: web-state-zustand description: Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.
Client State Management Patterns
> **Quick Guide:** Local UI state? useState. Shared UI (2+ components)? Zustand. Server data? Use your data fetching solution. URL-appropriate filters? searchParams. NEVER use Context for state management. Zustand v5: use `useShallow` from `zustand/react/shallow` (not the old equality-fn second arg), selectors must return stable references, and `persist` no longer stores initial state during creation.
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Store setup, selectors, useShallow, Context anti-patterns, URL state
---
<critical_requirements>
CRITICAL: Before Managing Client State
**(You MUST use a data fetching solution for ALL server/API data - NEVER useState, Zustand, or Context)**
**(You MUST use Zustand for ALL shared UI state (2+ components) - NOT Context or prop drilling)**
**(You MUST use useState ONLY for truly component-local state - NOT for anything shared)**
**(You MUST use atomic selectors or `useShallow` from `zustand/react/shallow` - NEVER destructure the entire store)**
**(You MUST ensure selectors return stable references - inline object/function creation causes infinite loops in v5)**
</critical_requirements>
---
**Auto-detection:** Zustand, zustand, create from zustand, useShallow, zustand/middleware, zustand store, client state, shared UI state, Context misuse, prop drilling, global state
**When to use:**
- Deciding between Zustand or useState for a use case
- Setting up Zustand for shared UI state (modals, sidebars, preferences)
- Understanding when NOT to use Context for state management
- Structuring stores: slices, actions, selectors
**Key patterns covered:**
- Client state = useState (local) or Zustand (shared, 2+ components)
- Context for dependency injection only (NEVER for state management)
- Store setup with devtools and persist middleware
- Selector patterns: atomic selectors vs useShallow
- URL params for shareable/bookmarkable state (filters, search)
**When NOT to use:**
- Server/API data (use a dedicated data fetching solution)
- State that should be shareable via URL (use searchParams)
- Any Context-based state management approach
---
<philosophy>
Philosophy
Zustand is a minimal, hook-based state manager. The key principle: **use the right tool for the right job**. Server data belongs in a dedicated data fetching layer with caching and synchronization. Local UI state stays in useState. Shared UI state lives in Zustand for performance. URL state makes filters shareable. Context is ONLY for dependency injection, never state management.
**Store design principles** (from TkDodo and official docs):
- **Keep stores small** - multiple focused stores beat one monolithic store
- **Business logic in the store** - components call actions, stores decide what happens
- **Only export custom hooks** - never expose the raw store creator
- **Atomic selectors preferred** - return single values, not objects, for best performance
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: State Placement Decision
The most critical decision: where does this state belong?
Is it server data (from API)?
├─ YES → Data fetching solution (not this skill's scope)
└─ NO → Is it URL-appropriate (filters, search)?
├─ YES → URL params (searchParams)
└─ NO → Is it needed in 2+ components?
├─ YES → Zustand
└─ NO → Is it truly component-local?
├─ YES → useState
└─ NO → Is it a singleton/dependency?
└─ YES → Context (ONLY for DI, not state)For full examples, see [examples/core.md](examples/core.md#pattern-1-state-placement).
---
Pattern 2: Local State with useState
Use ONLY when state is truly component-local and never shared.
- State used ONLY in one component (isExpanded, isOpen)
- Temporary UI state that never needs to be shared
- As soon as a second component needs it, move to Zustand
For good/bad comparisons, see [examples/core.md](examples/core.md#pattern-2-local-state-with-usestate).
---
Pattern 3: Zustand Store Setup
Use as soon as state is needed in 2+ components across the tree.
// stores/ui-store.ts
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
const UI_STORAGE_KEY = "ui-storage";
interface UIState {
sidebarOpen: boolean;
theme: "light" | "dark";
toggleSidebar: () => void;
setTheme: (theme: "light" | "dark") => void;
}
export const useUIStore = create<UIState>()(
devtools(
persist(
(set) => ({
sidebarOpen: true,
theme: "light",
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
setTheme: (theme) => set({ theme }),
}),
{ name: UI_STORAGE_KEY, partialize: (s) => ({ theme: s.theme }) },
),
),
);**Key points:** devtools for debugging, persist only what survives sessions (preferences, not transient UI), `partialize` to exclude ephemeral state.
For selectors, useShallow, and v5 stability patterns, see [examples/core.md](examples/core.md#pattern-3-zustand-store-setup).
---
Pattern 4: Context API - Dependency Injection ONLY
Context is NOT a state management solution. It's for dependency injection and singletons ONLY.
**ONLY use Context for:**
- Framework providers (router, query client)
- Dependency injection (services, API clients, DB connections)
- Values set once at app initialization that never change
**NEVER use Context for:**
- ANY state management (use Zustand instead)
- ANY frequently updating values (every consumer re-renders on any change)
For why Context fails for state and acceptable DI usage, see [examples/core.md](examples/core.md#pattern-4-context-api---dependency-injection-only).
---
Pattern 5: URL State for Shareable Filters
Use URL params
Showing the first part of this file.
The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?
Repo: agents-inc/skills
Other skills on agents-inc-skills.
- /ai-infrastructure-huggingface-inference
Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints
Open skill - /ai-infrastructure-litellm
LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment
Open skill - /ai-infrastructure-modal
Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns
Open skill - /ai-infrastructure-ollama
Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint
Open skill - /ai-infrastructure-replicate
Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training
Open skill - /ai-infrastructure-together-ai
Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints
Open skill

