本文へ移動
cccskills
無料GitHub で公開

web-state-zustand

Zustand stores, client state patterns. Use when deciding between Zustand vs useState, managing global state, or avoiding Context misuse.

インストール方法を見る

含まれるファイル(4)

  • SKILL.md7.7 KB
  • examples/core.md5.2 KB
  • metadata.yaml362 B
  • reference.md2.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Zustand Patterns

Quick Guide: Zustand owns state two or more components read; useState owns state one component reads; the URL owns filters and search; Context injects singletons and holds no state. Zustand v5 changes three things: useShallow from zustand/react/shallow replaces the old equality-function second argument, selectors must return stable references or they loop, and persist no longer captures initial state at creation.

Detailed Resources:

  • examples/core.md — store setup, atomic selectors, useShallow, the Context anti-pattern, URL state
  • reference.md — middleware lookup, v5 migration notes, peer-dependency requirements

<critical_requirements>

Before writing Zustand code

Move a value into a store as soon as a second component reads it. Passing it down instead couples every component on the path, and relocating it later means unpicking that chain.

Select one value per useStore call. The component then re-renders when that value changes and at no other time; a call with no selector subscribes to the whole store.

Return a stable reference from every selector. An object or function built inside the selector is a new reference on each call, which in v5 re-renders without end.

Persist preferences and nothing else, through partialize. Transient UI restored from storage — a modal that reopens itself, a sidebar that remembers being closed — reads as a bug.

Keep server data out of the store. Cached, invalidated, refetched data wants a layer built for it; a store gives you a copy that goes stale silently.

</critical_requirements>


Auto-detection: create from zustand, zustand/middleware, zustand/react/shallow, useShallow, createWithEqualityFn, zustand/traditional, persist, partialize, devtools, store slices

Applies to:

  • Choosing between a store, useState, the URL and Context for a given value
  • Setting up a store with devtools and persist
  • Selector shape and re-render behaviour
  • Splitting state across focused stores

Handled elsewhere:

  • Server data — caching, invalidation and refetching belong to whatever owns the network, and a store is not that thing
  • Routing mechanics — this skill says which state belongs in the URL, not how a given router reads or writes it
  • Form field state — a form library owns its own field values while the form is open

<philosophy>

Zustand is a subscription primitive with a hook attached. Its performance comes entirely from the selector: the store notifies every subscriber on every change, and the selector is what decides whether that notification becomes a render. So store design is selector design.

  • Small stores over one large one — a store is the unit of subscription as well as the unit of code
  • Business logic in actions — components call toggleSidebar(); the store decides what toggling means
  • Export hooks, not the creator — the raw store is a mutable global, and exporting it invites writes from anywhere
  • Atomic selectors first — a single value needs no comparison function at all
</philosophy>

<decision_framework>

Where a value belongs

The valueBelongs inBecause
Read by 2+ componentsa storeselective re-renders, and no prop chain to unpick later
Read by 1 componentuseStatenothing else can observe it, so nothing else needs to
A filter, query, page or sortthe URLshareable, bookmarkable, and the back button works
A singleton set at startupContextit never changes, so the re-render cost never arises
Fetched from an APInot this skillit needs caching and invalidation, which a store has none of

Context is the row people get wrong. It is a transport for a value that does not change, and using it for state that does re-renders every consumer on every change with no way to opt out.

</decision_framework>


<patterns>

Core patterns

Pattern 1: Store Setup

devtools for inspection, persist for the fields that should outlive the tab, partialize to keep everything else out of storage.

export const useUIStore = create<UIState>()(
  devtools(
    persist(
      (set) => ({
        theme: DEFAULT_THEME,
        sidebarOpen: true,
        toggleSidebar: () =>
          set((state) => ({ sidebarOpen: !state.sidebarOpen })),
      }),
      { name: UI_STORAGE_KEY, partialize: (state) => ({ theme: state.theme }) },
    ),
  ),
);

Full code: examples/core.md

Pattern 2: Atomic Selectors

One value per call is the default, and the reason the store is fast.

const toggleSidebar = useUIStore((state) => state.toggleSidebar);
const isOpen = useUIStore((state) => state.sidebarOpen);

Full code: examples/core.md

Pattern 3: useShallow for Multiple Values

At three or more values from one store, one useShallow call beats three subscriptions.

import { useShallow } from "zustand/react/shallow";

const { sidebarOpen, theme } = useUIStore(
  useShallow((state) => ({
    sidebarOpen: state.sidebarOpen,
    theme: state.theme,
  })),
);

Full code: examples/core.md

Pattern 4: Context for Injection, Not State

Context carries a value that never changes — a client, a connection, a configuration read at startup.

const DatabaseContext = createContext<Database | null>(null);

Full code: examples/core.md

Pattern 5: URL State for Shareable Filters

Filters, search, pagination and sort live in the URL, where they survive a reload and a paste into a colleague's chat.

const searchParams = new URLSearchParams(window.location.search);
const category = searchParams.get("category") ?? undefined;

Full code: examples/core.md

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • A selector returning an inline object or function — new reference every call, so v5 re-renders without end. Hoist the fallback to a module constant.
  • create() called with a second equality-function argument — removed in v5, so the argument is ignored and the comparison silently never happens. Use useShallow, or createWithEqualityFn from zustand/traditional.
  • Reading a computed initial value back out of a persisted store immediately after creation — v5's persist did not store it. Set it with setState after creation.

Surprising behaviour:

  • useStore() with no selector subscribes to the entire store, so the component re-renders on changes to fields it never reads.
  • Two atomic selectors returning equal values still render separately if either changes; useShallow compares the object, not the individual reads.
  • Context re-renders every consumer whenever the provider's value object is rebuilt, which is every render unless the value is memoised — and even memoised, a change to one field re-renders readers of all of them.
  • Search params are strings. ?page=2 reads back as "2", and ?open=false is truthy until parsed.
  • One large store means one large subscription surface: every component reading from it competes with every write to it.

</red_flags>

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

agents-inc のスキルをすべて見る

このスキルの問題を報告する