web-state-zustand
Zustandは、useStateの使い分けやグローバルな状態管理、Contextの誤用を防ぐ際に役立ち、クライアントの状態パターンを効率的に管理するSkill。
📜 元の英語説明(参考)
Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.
🇯🇵 日本人クリエイター向け解説
Zustandは、useStateの使い分けやグローバルな状態管理、Contextの誤用を防ぐ際に役立ち、クライアントの状態パターンを効率的に管理するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o web-state-zustand.zip https://jpskill.com/download/10307.zip && unzip -o web-state-zustand.zip && rm web-state-zustand.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10307.zip -OutFile "$d\web-state-zustand.zip"; Expand-Archive "$d\web-state-zustand.zip" -DestinationPath $d -Force; ri "$d\web-state-zustand.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
web-state-zustand.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
web-state-zustandフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
クライアント状態管理パターン
クイックガイド: ローカル UI 状態ですか? useState です。共有 UI (2つ以上のコンポーネント)ですか? Zustand です。サーバーデータですか? データ取得ソリューションを使用してください。URL に適したフィルターですか? searchParams です。状態管理に Context を絶対に使用しないでください。Zustand v5:
zustand/react/shallowのuseShallowを使用してください (古い equality-fn の第二引数ではありません)。セレクターは安定した参照を返し、persistは作成時に初期状態を保存しなくなりました。
詳細なリソース:
- examples/core.md - ストアのセットアップ、セレクター、useShallow、Context のアンチパターン、URL 状態
<critical_requirements>
重要: クライアント状態を管理する前に
(すべてのサーバー/API データにはデータ取得ソリューションを必ず使用してください - useState、Zustand、または Context は絶対に使用しないでください)
(すべての共有 UI 状態 (2つ以上のコンポーネント) には Zustand を必ず使用してください - Context や prop drilling は使用しないでください)
(useState は、真にコンポーネントローカルな状態にのみ使用してください - 共有されるものには使用しないでください)
(アトミックセレクターまたは zustand/react/shallow の useShallow を必ず使用してください - ストア全体を分割代入しないでください)
(セレクターが安定した参照を返すことを必ず確認してください - インラインのオブジェクト/関数作成は v5 で無限ループを引き起こします)
</critical_requirements>
自動検出: Zustand, zustand, create from zustand, useShallow, zustand/middleware, zustand store, client state, shared UI state, Context misuse, prop drilling, global state
使用するタイミング:
- Zustand または useState をユースケースに応じて選択する場合
- 共有 UI 状態 (モーダル、サイドバー、設定) のために Zustand をセットアップする場合
- 状態管理に Context を使用してはいけない場合を理解する場合
- ストアの構造化: スライス、アクション、セレクター
カバーされる主要なパターン:
- クライアント状態 = useState (ローカル) または Zustand (共有、2つ以上のコンポーネント)
- 依存性注入のみを目的とした Context (状態管理には絶対に使用しない)
- devtools と persist ミドルウェアを使用したストアのセットアップ
- セレクターパターン: アトミックセレクター vs useShallow
- 共有/ブックマーク可能な状態のための URL パラメータ (フィルター、検索)
使用しないタイミング:
- サーバー/API データ (専用のデータ取得ソリューションを使用)
- URL 経由で共有可能であるべき状態 (searchParams を使用)
- Context ベースの状態管理アプローチ
<philosophy>
哲学
Zustand は、最小限のフックベースの状態マネージャーです。重要な原則: 適切なジョブには適切なツールを使用する。サーバーデータは、キャッシュと同期を備えた専用のデータ取得レイヤーに属します。ローカル UI 状態は useState に保持されます。共有 UI 状態は、パフォーマンスのために Zustand に存在します。URL 状態により、フィルターを共有可能にします。Context は依存性注入のみを目的としており、状態管理には絶対に使用しません。
ストア設計の原則 (TkDodo と公式ドキュメントより):
- ストアを小さく保つ - 複数の焦点を絞ったストアは、1つのモノリシックなストアよりも優れています
- ストア内のビジネスロジック - コンポーネントはアクションを呼び出し、ストアは何が起こるかを決定します
- カスタムフックのみをエクスポートする - 生のストアクリエーターを公開しないでください
- アトミックセレクターが推奨される - 最高のパフォーマンスを得るために、オブジェクトではなく単一の値を返します
</philosophy>
<patterns>
コアパターン
パターン 1: 状態の配置の決定
最も重要な決定: この状態はどこに属するのか?
サーバーデータ (API から) ですか?
├─ YES → データ取得ソリューション (このスキルの範囲外)
└─ NO → URL に適していますか (フィルター、検索)?
├─ YES → URL パラメータ (searchParams)
└─ NO → 2つ以上のコンポーネントで必要ですか?
├─ YES → Zustand
└─ NO → 真にコンポーネントローカルですか?
├─ YES → useState
└─ NO → シングルトン/依存関係ですか?
└─ YES → Context (DI のみ、状態ではない)
完全な例については、examples/core.md を参照してください。
パターン 2: useState を使用したローカル状態
状態が真にコンポーネントローカルであり、決して共有されない場合にのみ使用してください。
- 1つのコンポーネントでのみ使用される状態 (isExpanded, isOpen)
- 共有する必要のない一時的な UI 状態
- 2番目のコンポーネントが必要になったらすぐに、Zustand に移行します
良い/悪い比較については、examples/core.md を参照してください。
パターン 3: Zustand ストアのセットアップ
ツリー全体で 2つ以上のコンポーネントで状態が必要になったらすぐに使用してください。
// 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 }) },
),
),
);
重要なポイント: デバッグ用の devtools、セッションを維持するもののみを永続化 (一時的な UI ではなく設定)、一時的な状態を除外するための partialize。
セレクター、useShallow、および v5 の安定性パターンについては、examples/core.md を参照してください。
パターン 4: Context API - 依存性注入のみ
Context は状態管理ソリューションではありません。依存性注入とシングルトンのみを目的としています。
Context は以下の場合にのみ使用してください:
- フレームワークプロバイダー (ルーター、クエリクライアント)
- 依存性注入 (サービス、API クライアント、DB 接続)
- アプリの初期化時に一度設定され、決して変更されない値
Context を絶対に使用しないでください:
- 状態管理 (代わりに Zustand を使用)
- 頻繁に更新される値 (変更があるたびにすべてのコンシューマーが再レンダリングされる)
Context が状態管理に失敗する理由と、許容される DI の使用法については、examples/core.md を参照してください。
パターン 5: 共有可能なフィルターのための URL 状態
共有可能、ブックマーク可能、またはナビゲート可能であるべき状態には、URL パラメータ (searchParams) を使用してください。
- フィルターの選択、検索クエリ、ページネーション、ソート順
- ブラウザの戻る/進むが正しく動作します
- U
(原文はここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
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
useShallowfromzustand/react/shallow(not the old equality-fn second arg), selectors must return stable references, andpersistno longer stores initial state during creation.
Detailed Resources:
- 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.
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.
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.
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.
Pattern 5: URL State for Shareable Filters
Use URL params (searchParams) for state that should be shareable, bookmarkable, or navigable.
- Filter selections, search queries, pagination, sort order
- Browser back/forward works correctly
- URLs can be shared with specific filter state
For implementation examples, see examples/core.md.
</patterns>
<decision_framework>
Decision Framework
Quick Reference Table
| Use Case | Solution | Why |
|---|---|---|
| Server/API data | Data fetching solution | Caching, synchronization, loading states |
| Shareable filters | URL params | Bookmarkable, browser navigation |
| Shared UI state (2+ components) | Zustand | Fast, selective re-renders, no prop drilling |
| Local UI state (1 component) | useState | Simple, component-local |
| Framework providers / DI | Context | Singletons that never change |
| ANY state management | NEVER Context | Causes full re-renders on any change |
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Storing server/API data in client state (useState, Context, Zustand) - causes stale data, no caching, manual sync complexity
- Using Context with useState/useReducer for state management - every consumer re-renders on any change, performance nightmare
- Destructuring the entire store
const { x, y } = useStore()- subscribes to all changes, defeats selective re-rendering - Using useState for state needed in 2+ components - causes prop drilling, tight coupling, refactoring difficulty
Medium Priority Issues:
- Prop drilling 3+ levels instead of using Zustand
- Filter state in useState instead of URL params (not shareable/bookmarkable)
- Creating unnecessary object references in Zustand selectors (causes re-renders)
- One monolithic store instead of multiple focused stores
Gotchas & Edge Cases:
- Context re-renders ALL consumers when ANY value changes - no way to select specific values
- Zustand selectors that return new objects cause re-renders even if values are identical - use
useShallowfromzustand/react/shallowor atomic selectors - URL params are always strings - need parsing for numbers/booleans
- Persisting modal/sidebar state across sessions confuses users - only persist preferences
- Zustand v5: Selectors must return stable references - returning new functions/objects inline causes infinite loops
- Zustand v5: The old
shallowsecond argument tocreate()is removed - useuseShallowhook wrapper orcreateWithEqualityFnfromzustand/traditional - Zustand v5: The persist middleware no longer stores initial state during creation - set computed/random initial values explicitly with
useStore.setState() - Zustand v5: Requires React 18+ and TypeScript 4.5+
- Zustand v5:
use-sync-external-storeis a peer dependency only when usingzustand/traditional
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
(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)
Failure to follow these rules will cause stale data issues, performance problems, and infinite render loops.
</critical_reminders>