State Management
Use this skill when the real question is:
What packet is this state, who should own it, and what tempting wrong owner should we avoid?
Do not start with a library debate. Start by naming one primary packet, then choose the smallest owner that matches its lifecycle.
Read references/ownership-packets-and-route-outs.md before handling a mixed or ambiguous request.
Read references/decision-matrix.md when the packet is clear and you need to compare likely owners.
Read references/handoff-boundaries.md when the request may actually belong to react-best-practices, api-design, debugging, design-system, design-system, or responsive-design.
When to use this skill
- Decide whether a frontend problem is local UI state, shared subtree state, URL/navigation state, form state, server state, or long-lived client workflow state.
- Turn vague requests like “we need state management” into a concrete ownership recommendation.
- Choose between Context, Zustand, Redux Toolkit, Jotai, TanStack Query, or router-native ownership without treating them as interchangeable.
- Stop “one store for everything” proposals before implementation starts.
- Produce a short ownership brief for implementation or review.
When not to use this skill
- The main task is rerender churn, hydration mismatch, waterfalls, or client/server performance behavior → use
react-best-practices.
- The main task is mutation shape, backend ownership, or optimistic-write API design → use
api-design.
- The main task is debugging stale closures, races, broken optimistic updates, or existing state bugs → use
debugging.
- The main task is controlled/uncontrolled component APIs, primitives, slots, or reusable component contracts → use
design-system.
- The main task is visual system governance or shared preference policy → use
design-system.
- The main task is viewport adaptation or layout collapse → use
responsive-design.
- The architecture choice is already made and the user just needs code → implement directly instead of re-running the chooser.
Instructions
Step 1: Pick one primary packet
Choose the main packet before naming any tool:
- local-ui — toggles, open panels, inline edit state, active row, one-feature transient state
- shared-subtree — theme, auth snapshot, locale, feature flags, moderate shell state
- url-navigation — filters, sort order, pagination, selected tab, deep-linkable view state
- form-lifecycle — dirty/touched state, validation, submission lifecycle, async submit errors
- server-state — fetched data, caching, invalidation, revalidation, optimistic server updates
- client-workflow — cross-page drafts, undo, multi-step coordination, offline edits, long-lived workflow state
Optional: list one or two secondary packets, but force a single primary packet.
Quick frame:
Primary packet: server-state
Secondary packets: url-navigation, local-ui
Decision pressure: shareable filters + remote list freshness
Step 2: Choose the smallest owner
Use the narrowest owner that matches the packet:
- local-ui → local state, lifted state, or
useReducer
- shared-subtree → Context when one moderate tree needs a shared source of truth
- url-navigation → router/search params when refresh, deep links, and back/forward should work naturally
- form-lifecycle → form-layer ownership, not app-wide global state
- server-state → TanStack Query or router-native data APIs when cache/revalidation rules dominate
- client-workflow → Zustand, Redux Toolkit, Jotai, or explicit state-machine modeling only after the earlier packets are separated
Rule: if the state is really route-shaped or server-shaped, do not hide it in a generic client store just because the store already exists.
Step 3: Run the anti-pattern check
Call out the most likely wrong owner explicitly:
- one universal store for unrelated lifecycles
- mirroring server cache into a client store without an offline/workflow reason
- hiding shareable filters or tabs outside the URL
- pushing form dirty/touched/submit lifecycle into global app state
- treating every slow or buggy UI issue as a state-architecture problem
A good answer names both the chosen owner and the tempting wrong owner.
Step 4: Compare client-store options only if a client-workflow packet remains
After local/shared/URL/form/server packets are separated:
- Context when the real pain is access/distribution across a moderate tree
- Zustand when you need low-ceremony cross-component or cross-page workflow state
- Redux Toolkit when workflows are coupled, event-heavy, or need stronger conventions and auditability
- Jotai when fine-grained atom composition truly matches the app’s mental model
- State machines / explicit workflow modeling when transitions, guards, and multi-step coordination dominate
Do not compare stores for packets that already belong to URL/form/server ownership.
Step 5: Name mixed architectures directly
Healthy combinations are normal:
- URL state + query/router data + local UI state
- Context + query/router data
- Zustand + query/router data
- Redux Toolkit + query/router data
- form state + URL state + query/router data
If the request asks for one universal store, explain what gets worse when unlike lifecycles are flattened together.
Step 6: Route adjacent work out immediately
Stay here only while choosing ownership.
Route out when the next step is:
react-best-practices for rerender/hydration/waterfall/perf issues
api-design for mutation contracts or backend/client responsibility
debugging for already-broken state behavior
design-system for component API ownership
design-system for system-level preference/governance rules
responsive-design for layout/viewport behavior
Step 7: Produce one ownership brief
Use this format:
Primary packet:
Secondary packets:
Recommended owners:
- ...
Why this is the smallest viable split:
- ...
Avoid:
- ...
Route-out note:
- ...
Next implementation step:
- ...
Keep it short. If the honest answer is “do less and keep the state closer to where it lives,” say that directly.
Output format
Return a concise ownership recommendation with:
- one primary packet and any secondary packets
- chosen owners per packet
- one-paragraph rationale
- explicit anti-patterns to avoid
- route-out note if another skill should own the next step
Examples
Example 1: Dashboard data, filters, and modals
Input: “Should this dashboard use Zustand or TanStack Query for project data, filters, and modal state?”
Good output shape:
- primary packet = server-state
- secondary packets = url-navigation, local-ui
- project data = TanStack Query or router-native data ownership
- filters = URL/search params if shareable/bookmarkable
- modal state = local or lightweight client state only if coordination spans distant branches
- avoid mirroring fetched entities into Zustand without an offline/workflow reason
Example 2: Prop drilling theme and auth
Input: “Theme and auth are passed through five levels. Do we need Redux?”
Good output shape:
- primary packet = shared-subtree
- recommend Context first
- reject Redux unless broader coupled workflow state also exists
Example 3: React Router app storing filters and loader data in Zustand
Input: “Our React Router app stores filters and loader data in Zustand. Is that fine?”
Good output shape:
- primary packet = url-navigation or server-state, not client-workflow
- move shareable filters into URL/search params
- keep loader/action data in router-native data ownership unless a separate cache need is real
- use Zustand only for leftover workflow coordination
Example 4: Broken optimistic updates
Input: “The state code is buggy and optimistic updates are broken—what skill owns the next step?”
Good output shape:
- identify that ownership choice is secondary to active failure diagnosis
- route the next step to
debugging
- mention
api-design if the optimistic-write contract itself is the real dispute
Best practices
- Pick the packet before the tool.
- Use the smallest viable owner.
- Keep URL/form/server/client workflow lifecycles separate unless there is a strong reason not to.
- Call out the wrong owner explicitly.
- Treat mixed ownership as normal, not as a failure to standardize.
- End with a brief that an implementer or reviewer can act on immediately.
References
1---2name: state-management3description: Choose the smallest viable React/fullstack state owner before comparing libraries. Use when the user needs to classify local UI state, shared subtree/Context state, URL or form state, server-state caches, or client workflow stores such as Zustand / Redux Toolkit / Jotai. Triggers on: global state, prop drilling, Context vs Zustand, Redux Toolkit vs Zustand, server state vs client state, React Router state management, optimistic updates, URL state, form state, and too much state in one store.4license: MIT5---67# State Management89Use this skill when the real question is:1011**What packet is this state, who should own it, and what tempting wrong owner should we avoid?**1213Do **not** start with a library debate. Start by naming one primary packet, then choose the smallest owner that matches its lifecycle.1415Read [references/ownership-packets-and-route-outs.md](references/ownership-packets-and-route-outs.md) before handling a mixed or ambiguous request.16Read [references/decision-matrix.md](references/decision-matrix.md) when the packet is clear and you need to compare likely owners.17Read [references/handoff-boundaries.md](references/handoff-boundaries.md) when the request may actually belong to `react-best-practices`, `api-design`, `debugging`, `design-system`, `design-system`, or `responsive-design`.1819## When to use this skill20- Decide whether a frontend problem is local UI state, shared subtree state, URL/navigation state, form state, server state, or long-lived client workflow state.21- Turn vague requests like “we need state management” into a concrete ownership recommendation.22- Choose between Context, Zustand, Redux Toolkit, Jotai, TanStack Query, or router-native ownership without treating them as interchangeable.23- Stop “one store for everything” proposals before implementation starts.24- Produce a short ownership brief for implementation or review.2526## When not to use this skill27- **The main task is rerender churn, hydration mismatch, waterfalls, or client/server performance behavior** → use `react-best-practices`.28- **The main task is mutation shape, backend ownership, or optimistic-write API design** → use `api-design`.29- **The main task is debugging stale closures, races, broken optimistic updates, or existing state bugs** → use `debugging`.30- **The main task is controlled/uncontrolled component APIs, primitives, slots, or reusable component contracts** → use `design-system`.31- **The main task is visual system governance or shared preference policy** → use `design-system`.32- **The main task is viewport adaptation or layout collapse** → use `responsive-design`.33- **The architecture choice is already made and the user just needs code** → implement directly instead of re-running the chooser.3435## Instructions3637### Step 1: Pick one primary packet38Choose the main packet before naming any tool:39- **local-ui** — toggles, open panels, inline edit state, active row, one-feature transient state40- **shared-subtree** — theme, auth snapshot, locale, feature flags, moderate shell state41- **url-navigation** — filters, sort order, pagination, selected tab, deep-linkable view state42- **form-lifecycle** — dirty/touched state, validation, submission lifecycle, async submit errors43- **server-state** — fetched data, caching, invalidation, revalidation, optimistic server updates44- **client-workflow** — cross-page drafts, undo, multi-step coordination, offline edits, long-lived workflow state4546Optional: list one or two secondary packets, but force a single **primary** packet.4748Quick frame:49```markdown50Primary packet: server-state51Secondary packets: url-navigation, local-ui52Decision pressure: shareable filters + remote list freshness53```5455### Step 2: Choose the smallest owner56Use the narrowest owner that matches the packet:57- **local-ui** → local state, lifted state, or `useReducer`58- **shared-subtree** → Context when one moderate tree needs a shared source of truth59- **url-navigation** → router/search params when refresh, deep links, and back/forward should work naturally60- **form-lifecycle** → form-layer ownership, not app-wide global state61- **server-state** → TanStack Query or router-native data APIs when cache/revalidation rules dominate62- **client-workflow** → Zustand, Redux Toolkit, Jotai, or explicit state-machine modeling only after the earlier packets are separated6364Rule: if the state is really route-shaped or server-shaped, do not hide it in a generic client store just because the store already exists.6566### Step 3: Run the anti-pattern check67Call out the most likely wrong owner explicitly:68- one universal store for unrelated lifecycles69- mirroring server cache into a client store without an offline/workflow reason70- hiding shareable filters or tabs outside the URL71- pushing form dirty/touched/submit lifecycle into global app state72- treating every slow or buggy UI issue as a state-architecture problem7374A good answer names both the chosen owner **and** the tempting wrong owner.7576### Step 4: Compare client-store options only if a client-workflow packet remains77After local/shared/URL/form/server packets are separated:78- **Context** when the real pain is access/distribution across a moderate tree79- **Zustand** when you need low-ceremony cross-component or cross-page workflow state80- **Redux Toolkit** when workflows are coupled, event-heavy, or need stronger conventions and auditability81- **Jotai** when fine-grained atom composition truly matches the app’s mental model82- **State machines / explicit workflow modeling** when transitions, guards, and multi-step coordination dominate8384Do not compare stores for packets that already belong to URL/form/server ownership.8586### Step 5: Name mixed architectures directly87Healthy combinations are normal:88- URL state + query/router data + local UI state89- Context + query/router data90- Zustand + query/router data91- Redux Toolkit + query/router data92- form state + URL state + query/router data9394If the request asks for one universal store, explain what gets worse when unlike lifecycles are flattened together.9596### Step 6: Route adjacent work out immediately97Stay here only while choosing ownership.9899Route out when the next step is:100- **`react-best-practices`** for rerender/hydration/waterfall/perf issues101- **`api-design`** for mutation contracts or backend/client responsibility102- **`debugging`** for already-broken state behavior103- **`design-system`** for component API ownership104- **`design-system`** for system-level preference/governance rules105- **`responsive-design`** for layout/viewport behavior106107### Step 7: Produce one ownership brief108Use this format:109```markdown110Primary packet:111Secondary packets:112113Recommended owners:114- ...115116Why this is the smallest viable split:117- ...118119Avoid:120- ...121122Route-out note:123- ...124125Next implementation step:126- ...127```128129Keep it short. If the honest answer is “do less and keep the state closer to where it lives,” say that directly.130131## Output format132Return a concise ownership recommendation with:1331. one primary packet and any secondary packets1342. chosen owners per packet1353. one-paragraph rationale1364. explicit anti-patterns to avoid1375. route-out note if another skill should own the next step138139## Examples140141### Example 1: Dashboard data, filters, and modals142**Input:** “Should this dashboard use Zustand or TanStack Query for project data, filters, and modal state?”143144**Good output shape:**145- primary packet = server-state146- secondary packets = url-navigation, local-ui147- project data = TanStack Query or router-native data ownership148- filters = URL/search params if shareable/bookmarkable149- modal state = local or lightweight client state only if coordination spans distant branches150- avoid mirroring fetched entities into Zustand without an offline/workflow reason151152### Example 2: Prop drilling theme and auth153**Input:** “Theme and auth are passed through five levels. Do we need Redux?”154155**Good output shape:**156- primary packet = shared-subtree157- recommend Context first158- reject Redux unless broader coupled workflow state also exists159160### Example 3: React Router app storing filters and loader data in Zustand161**Input:** “Our React Router app stores filters and loader data in Zustand. Is that fine?”162163**Good output shape:**164- primary packet = url-navigation or server-state, not client-workflow165- move shareable filters into URL/search params166- keep loader/action data in router-native data ownership unless a separate cache need is real167- use Zustand only for leftover workflow coordination168169### Example 4: Broken optimistic updates170**Input:** “The state code is buggy and optimistic updates are broken—what skill owns the next step?”171172**Good output shape:**173- identify that ownership choice is secondary to active failure diagnosis174- route the next step to `debugging`175- mention `api-design` if the optimistic-write contract itself is the real dispute176177## Best practices1781. Pick the packet before the tool.1792. Use the smallest viable owner.1803. Keep URL/form/server/client workflow lifecycles separate unless there is a strong reason not to.1814. Call out the wrong owner explicitly.1825. Treat mixed ownership as normal, not as a failure to standardize.1836. End with a brief that an implementer or reviewer can act on immediately.184185## References186- [React — Sharing State Between Components](https://react.dev/learn/sharing-state-between-components)187- [React — Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure)188- [React — Context](https://react.dev/learn/passing-data-deeply-with-context)189- [Redux FAQ — Organizing State](https://redux.js.org/faq/organizing-state)190- [Redux — Why Redux Toolkit is How To Use Redux Today](https://redux.js.org/introduction/why-rtk-is-redux-today)191- [TanStack Query — Does this replace client state managers?](https://tanstack.com/query/latest/docs/framework/react/guides/does-this-replace-client-state)192- [React Router — State Management](https://reactrouter.com/explanation/state-management)193- [Zustand](https://github.com/pmndrs/zustand)194- [Jotai](https://jotai.org/docs/basics/comparison)