nextjs-zustand について
Next.js 16 App Router アプリで Zustand v5 を使い、クライアント側の状態を管理するスキルです。リクエストを分離する Context ベースのストア、SSR のハイドレーション、ミドルウェア、ファイル構成を扱います。
主な機能
- 01Zustand v5 のストア作成構文
- 02Context ベースのストアによるリクエスト分離
- 03ミドルウェアの組み合わせ
- 04永続化と SSR ハイドレーションへの対応
- 05機能モジュール単位のストア構成
活用シーン
- 01複数のクライアントコンポーネント間の状態管理
- 02UI 状態の永続化とハイドレーション
- 03App Router プロジェクトでのストア構成
- 04セレクターによる不要な再レンダリングの回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fusengine/ claude-plugins --skill nextjs-zustandインストール後、Claude Code で/nextjs-zustandを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/auto-selectors.mdhydration.mdinstallation.mdmiddleware.mdmigration-v5.mdnextjs-integration.mdreset-state.mdslices.mdstore-patterns.mdsubscribe-api.mdtesting.mdtypescript.md詳細
persist ミドルウェアの skipHydration と、SSR の不整合を防ぐ useEffect 内での手動再ハイドレーション、配列やオブジェクトのセレクターに対する useShallow、SOLID に沿ったファイル構成(store.ts、store-provider.tsx、use-store.ts)を解説します。Zustand は Client Components 専用であり、Server Components では使用しないこと(代わりにそこでデータを直接取得します)、認証トークンの永続化には使用しないこと(httpOnly Cookie を使用します)を明記します。サーバー状態のキャッシュは対象外です(その場合は nextjs-tanstack-query を参照してください)。
Next.js 16 向け Zustand
React 18 以降の useSyncExternalStore を使用した、シンプルでスケーラブルな状態管理です。
エージェントのワークフロー(必須)
実装を始める前に、3 つのエージェントを並行して起動してください。それぞれ name を指定して Agent を 1 回呼び出します。
- fuse-ai-pilot:explore-codebase - 既存のストアと状態管理パターンを分析します
- fuse-ai-pilot:research-expert - Context7/Exa を使って最新の Zustand v5 ドキュメントを確認します
- mcp__context7__query-docs - ミドルウェアと TypeScript のパターンを確認します
実装後、検証のために fuse-ai-pilot:sniper を実行してください。
概要
使用する場面
- Next.js App Router アプリケーションでクライアントサイドの状態を管理する場合
- Client Components の間だけで共有するグローバル状態が必要な場合
- 状態を localStorage/sessionStorage に永続化する場合
- UI の状態(モーダル、サイドバー、テーマ、カートなど)を構築する場合
- 複雑な状態管理で React Context を置き換える場合
Zustand v5 を選ぶ理由
| 特徴 | メリット |
|---|---|
| 最小限の API | シンプルな create() 関数で、ボイラープレートが不要です |
| React 18 ネイティブ | useSyncExternalStore を使用し、シムは不要です |
| TypeScript 優先 | カリー化パターンで完全な型推論が得られます |
| ミドルウェアスタック | devtools、persist、immer を組み合わせられます |
| バンドルサイズ | gzip 圧縮後で約 2KB と、状態管理ライブラリの中で最小です |
| プロバイダー不要 | Context ラッパーなしでストアに直接アクセスできます |
重要なルール
- Client Components 専用 - Server Components では Zustand を使用しないでください
- App Router では Context パターンを使用 - グローバルストアを避けます(リクエストを分離するため)
- 配列やオブジェクトには useShallow を使用 - 不要な再レンダリングを防ぎます
- persist では skipHydration を使用 - SSR との互換性に必須です
- v5 のカリー化構文を使用 -
create<State>()((set) => ({...})) - SOLID に沿った配置 - ストアは
modules/[feature]/src/stores/に配置します
SOLID アーキテクチャ
モジュール構成
ストアは機能モジュールごとに整理します。
modules/cores/stores/- 共有ストア(テーマ、UI)modules/auth/src/stores/- 認証状態modules/cart/src/stores/- カート状態modules/[feature]/src/interfaces/- ストアの型
ファイル構成
| ファイル | 目的 |
|---|---|
store.ts | create() によるストアの作成(最大 40 行、オーナールール) |
store.interface.ts | TypeScript インターフェース |
store-provider.tsx | Context プロバイダー(App Router) |
use-store.ts | セレクター付きカスタムフック |
主要な概念
ストアの作成(v5 構文)
TypeScript の型推論には二重の括弧が必要です。カリー化パターンによって完全な型安全性を確保できます。
Context ベースのストア
Next.js App Router では、リクエスト間で状態が共有されるのを防ぐため、ストアを Context でラップします。初期化には useRef と zustand/vanilla の createStore を使用します。
ミドルウェアの合成
ミドルウェアを devtools → persist → immer の順に重ねます。順序は TypeScript の型に影響します。
ハイドレーションの処理
persist ミドルウェアで skipHydration: true を使用します。SSR の不整合を防ぐため、useEffect 内で手動で再ハイドレーションします。
リファレンスガイド
| 必要な項目 | リファレンス |
|---|---|
| 初期設定 | installation.md |
| ストアのパターン | store-patterns.md |
| SSR/ハイドレーション | hydration.md |
| ミドルウェア | middleware.md |
| Next.js App Router | nextjs-integration.md |
| TypeScript | typescript.md |
| スライスパターン | slices.md |
| 自動セレクター | auto-selectors.md |
| 状態のリセット | reset-state.md |
| Subscribe API | subscribe-api.md |
| テスト | testing.md |
| v4 から v5 への移行 | migration-v5.md |
ベストプラクティス
- セレクターパターン - パフォーマンスのため、常に
useStore((s) => s.field)を使用します - useShallow - 再レンダリングを防ぐため、配列/オブジェクトのセレクターをラップします
- ストアを分ける - ドメインごとに1つのストアを用意します(認証、カート、UI、テーマ)
- サーバーデータは別の場所で管理する - サーバー状態には TanStack Query を使用します
- 開発時のみ DevTools を使用する - process.env のチェックで DevTools をラップします
- persist の対象を限定する - 必要なフィールドのみを永続化し、トークンは決して永続化しません
禁止されているパターン
| パターン | 理由 | 代替案 |
|---|---|---|
| App Router でのグローバルストア | ユーザー間でリクエストが共有されます | コンテキストベースのストア |
| Server Components での Zustand | RSC では React フックを使用できません | データを直接取得する |
| 認証トークンの永続化 | セキュリティ上の脆弱性 | httpOnly クッキー |
| オブジェクトに useShallow を使用しない | 過剰な再レンダリング | useShallow(selector) |
| v4 の構文 | TypeScript の型推論が壊れます | v5 のカリー化 create<T>()() |