jpskill.com
🛠️ 開発・MCP コミュニティ

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本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
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
🪟 Windows (PowerShell)
$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. 1. 下の青いボタンを押して web-state-zustand.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → web-state-zustand フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

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/shallowuseShallow を使用してください (古い equality-fn の第二引数ではありません)。セレクターは安定した参照を返し、persist は作成時に初期状態を保存しなくなりました。

詳細なリソース:

  • examples/core.md - ストアのセットアップ、セレクター、useShallow、Context のアンチパターン、URL 状態

<critical_requirements>

重要: クライアント状態を管理する前に

(すべてのサーバー/API データにはデータ取得ソリューションを必ず使用してください - useState、Zustand、または Context は絶対に使用しないでください)

(すべての共有 UI 状態 (2つ以上のコンポーネント) には Zustand を必ず使用してください - Context や prop drilling は使用しないでください)

(useState は、真にコンポーネントローカルな状態にのみ使用してください - 共有されるものには使用しないでください)

(アトミックセレクターまたは zustand/react/shallowuseShallow を必ず使用してください - ストア全体を分割代入しないでください)

(セレクターが安定した参照を返すことを必ず確認してください - インラインのオブジェクト/関数作成は 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 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 - 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 useShallow from zustand/react/shallow or 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 shallow second argument to create() is removed - use useShallow hook wrapper or createWithEqualityFn from zustand/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-store is a peer dependency only when using zustand/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>