
State Management
- 224 installs
- 40 repo stars
- Updated August 4, 2026
- akillness/oh-my-skills
Design and implement client state patterns for React or similar apps handling forms, async data, caches, and shared UI state across routes and components.
About
Teaches agents how to implement robust frontend state management for interactive applications, covering local state, shared stores, async updates, and component coordination. It supports building maintainable UIs with clear data flow during feature development.
- Client state pattern selection
- Async and form state handling
- Component-to-store wiring guidance
- Scalable UI data flow design
- Reduces ad-hoc prop drilling
State Management by the numbers
- 224 all-time installs (skills.sh)
- Ranked #832 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/akillness/oh-my-skills --skill state-managementAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 224 |
|---|---|
| repo stars | ★ 40 |
| Last updated | August 4, 2026 |
| Repository | akillness/oh-my-skills ↗ |
What it does
Design and implement client state patterns for React or similar apps handling forms, async data, caches, and shared UI state across routes and components.
Files
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, ui-component-patterns, 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
ui-component-patterns. - 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 freshnessStep 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
- `ui-component-patterns` 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: 1. one primary packet and any secondary packets 2. chosen owners per packet 3. one-paragraph rationale 4. explicit anti-patterns to avoid 5. 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-designif the optimistic-write contract itself is the real dispute
Best practices
1. Pick the packet before the tool. 2. Use the smallest viable owner. 3. Keep URL/form/server/client workflow lifecycles separate unless there is a strong reason not to. 4. Call out the wrong owner explicitly. 5. Treat mixed ownership as normal, not as a failure to standardize. 6. End with a brief that an implementer or reviewer can act on immediately.
References
{
"skill_name": "state-management",
"evals": [
{
"id": 1,
"prompt": "Should this React dashboard use Zustand or TanStack Query for API data, filters, and modal state?",
"expected_output": "Separates server state from URL/navigation and local UI state, recommends TanStack Query or equivalent for remote data, and warns against mirroring fetched entities into a client store without a reason.",
"assertions": [
"Output identifies one primary packet instead of starting with a library-only comparison.",
"Output classifies API data as server state.",
"Output recommends TanStack Query or equivalent query/cache ownership for fetched remote data.",
"Output treats filters as URL state when shareable or bookmarkable.",
"Output distinguishes modal/client UI state from remote cached data.",
"Output names at least one anti-pattern to avoid."
]
},
{
"id": 2,
"prompt": "We keep prop drilling theme and auth through five component layers. Do we need Redux?",
"expected_output": "Treats the problem as shared-subtree ownership first and recommends Context before Redux unless broader workflow complexity exists.",
"assertions": [
"Output identifies prop drilling as a shared-state ownership problem.",
"Output recommends Context or lifted/shared state before Redux Toolkit.",
"Output explains why Redux is not automatically required.",
"Output avoids framing the request as server-state or URL-state first."
]
},
{
"id": 3,
"prompt": "Our app has optimistic updates, undo, cross-page drafts, and complex workflow transitions. Which state approach fits?",
"expected_output": "Splits server state from long-lived client workflow state and compares Zustand versus Redux Toolkit based on coordination complexity, with room for explicit workflow/state-machine modeling if transitions dominate.",
"assertions": [
"Output separates server state from client workflow state.",
"Output discusses TanStack Query or router-native data ownership for cache/invalidation or optimistic server data handling.",
"Output compares at least two client-state options, including Redux Toolkit or Zustand.",
"Output produces a recommendation brief rather than only code snippets."
]
},
{
"id": 4,
"prompt": "Our React Router app stores filters and loader data in Zustand. Is that fine?",
"expected_output": "Recognizes URL/navigation or server-state ownership as primary, moves shareable filters to search params or router ownership, keeps loader/action data in router-native ownership unless a separate cache need exists, and limits Zustand to leftover workflow state.",
"assertions": [
"Output identifies URL/navigation or server-state as the main packet, not client-workflow by default.",
"Output recommends URL/search-param or router-native ownership for shareable filters.",
"Output warns against storing loader or action data in Zustand by default.",
"Output permits Zustand only for leftover workflow coordination if justified."
]
},
{
"id": 5,
"prompt": "The state code is buggy and optimistic updates are broken—what skill owns the next step?",
"expected_output": "Routes the next step to debugging because active failure diagnosis is now primary, while optionally noting api-design if the optimistic-write contract itself is the real disagreement.",
"assertions": [
"Output routes the active failure diagnosis to debugging.",
"Output does not answer only with a store recommendation.",
"Output may mention api-design when the contract/boundary is the real issue."
]
}
]
}
State Management Decision Matrix
Use this matrix after classifying the ownership packet.
| Ownership packet | Default owner | Escalate when | Typical anti-pattern |
|---|---|---|---|
| Local UI state | useState / useReducer / lifted state | multiple distant consumers need it or the state survives feature boundaries | putting every toggle and inline input into a global store |
| Shared subtree state | lift state / Context | write frequency, cross-page coordination, or event complexity outgrow Context | jumping to Redux/Zustand before proving Context is insufficient |
| URL/navigation state | router/search params/session-backed route state | deep-linkability, back/forward behavior, or framework-native data ownership matters | hiding shareable filters/tabs only inside a client store |
| Form state | form library or local reducer | validation, async submission, or dirty/touched lifecycle becomes non-trivial | storing touched/errors/submission status in the app-wide store |
| Server state | TanStack Query or router-native data/cache layer | offline drafts or long-lived client workflow staging require a separate client layer too | mirroring the full remote cache into Zustand/Redux for one-store aesthetics |
| Client workflow state | Zustand or Redux Toolkit | auditability, event modeling, transition complexity, or cross-feature coupling grows | forcing long-lived workflow state into Context with ad hoc setters |
| Explicit workflow / state machine | state-machine layer plus the relevant client/server owners | guards, transitions, retries, and multi-step flows dominate the reasoning | pretending a complex state machine is just another blob in one store |
Quick chooser
Context is usually enough when
- the shared surface is small to medium
- write frequency is moderate
- the problem is ownership and access, not workflow orchestration
- examples: theme, auth snapshot, locale, feature flags
Zustand is usually enough when
- the app needs lightweight shared client state across distant branches
- slices are relatively independent
- developer speed and low ceremony matter more than strict event modeling
- examples: panel coordination, client-only drafts, editor UI state, cross-page transient workflow state
Redux Toolkit is worth it when
- many contributors need explicit event-based structure
- the workflow has coupled transitions and richer debugging needs
- action history and disciplined patterns matter
- examples: complex admin workflows, multi-surface editing tools, high-coupling business processes
Jotai fits when
- atom-level composition matches the mental model of the app
- different state units evolve semi-independently
- the team can maintain discipline around atom boundaries
Router-native data/state is enough when
- the framework already owns loaders/actions/search params/sessions cleanly
- the main value is deep-linkability or route-driven revalidation
- adding a separate cache/store would duplicate what the router already knows
Mixed-mode defaults
- Dashboard: URL state + TanStack Query + local UI state
- Authenticated SaaS app: Context for auth/theme + TanStack Query for data + optional Zustand for cross-panel coordination
- Complex product workflow: TanStack Query for remote entities + Redux Toolkit or Zustand for client workflow state
- Search/results app: router/search params + route data layer + local form state
Red flags
- “Everything goes in one store” with no lifecycle distinction
- fetched API data duplicated into a second store without a clear reason
- Context used for high-churn complex workflow state just because it is dependency-free
- Redux chosen from habit when the real issue is only prop drilling
- URL/search-param state hidden in memory even though shareability and back/forward matter
Handoff Boundaries for state-management
Use state-management when the main question is who should own this data and what layer fits.
Stay in state-management when
- the user asks whether data is local, shared, URL-backed, form-owned, server-owned, or client-workflow state
- the user wants Context vs Zustand vs Redux Toolkit vs Jotai vs query/router guidance
- the user is mixing query caching, route data, and client stores and needs a boundary recommendation
- the user wants an architecture brief before implementation or review
Route to react-best-practices when
- the main pain is rerender churn, hydration mismatch, client/server component boundaries, waterfalls, or slow UI updates
- the recommendation already exists and the next step is performance implementation or verification
Route to api-design when
- the disagreement is really about mutation shape, response contracts, optimistic-write API design, or backend/client responsibility
- the issue is not who owns state in React, but what the API should return or accept
Route to debugging when
- the current app already has stale closures, race conditions, broken optimistic updates, inconsistent synchronization, or weird store behavior and needs diagnosis
- the user needs reproduction, isolation, or verification of a bug rather than a fresh architecture choice
Route to ui-component-patterns when
- the main task is controlled/uncontrolled component APIs, reusable primitives, slots, compound components, or one component family's interface
- state ownership is subordinate to component contract design
Route to design-system when
- the task is really about shared breakpoint policy, theme/density governance, token rules, or cross-product preference strategy
- the runtime owner is secondary to the system-level rule or governance decision
Route to responsive-design when
- the main question is viewport adaptation, layout collapse, overflow, breakpoint behavior, or mobile/tablet strategy
- state ownership matters only as one detail inside a broader responsive layout decision
Rule of thumb
- Which owner should this state have? →
state-management - Why is the current state code malfunctioning? →
debugging - Why is this UI slow or rerendering too much? →
react-best-practices - What should the API or mutation contract look like? →
api-design - How should the component interface behave? →
ui-component-patterns - What system rule should multiple products/components follow? →
design-system - How should the layout adapt across widths/devices? →
responsive-design
Ownership packets and route-outs
Use this note when a request looks like “state management” but actually mixes several different owners.
Packet-first reading order
1. URL / route packet — Does navigation, deep-linkability, back/forward behavior, or route-native data loading already own this? 2. Form packet — Is this mostly validation, touched/dirty state, submission lifecycle, or async submit error handling? 3. Server-state packet — Is the real problem fetching, caching, invalidation, optimistic updates, revalidation, or remote freshness? 4. Shared subtree packet — Is Context enough for theme/auth/locale/shell state across a moderate tree? 5. Client workflow packet — After the earlier packets are separated, what long-lived client coordination remains? 6. Local UI packet — What transient state can stay nearest to the feature instead of being promoted at all?
Rule: choose one primary packet before comparing tools. Secondary packets are fine, but do not skip the primary-owner call.
Healthy packet combinations
Dashboard / admin surface
- URL owns filters, sort, pagination
- query/router data layer owns fetched entities and invalidation
- local or lightweight client state owns panel toggles and transient coordination
Authenticated SaaS shell
- Context owns auth snapshot, theme, locale
- query/router data layer owns remote resources
- optional lightweight client store owns cross-panel transient workflow state
Multi-step editing flow
- form layer owns validation and submission state
- query/router data layer owns remote entities and optimistic invalidation
- client workflow state owns drafts, undo, and multi-step coordination
- explicit state-machine modeling may be justified if transitions/guards dominate
Wrong-owner checklist
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 and tabs outside the URL
- pushing dirty/touched/form-submit lifecycle into app-wide global state
- using a store debate to avoid naming whether the real task is performance, debugging, API design, or layout behavior
Route-out checklist
- `react-best-practices` if the question is mostly rerenders, hydration, or server/client performance cost
- `api-design` if mutation contracts or backend responsibility drive the disagreement
- `debugging` if the current behavior is already broken and needs reproduce → isolate → verify
- `ui-component-patterns` if one component API is the real decision surface
- `design-system` if shared preference/governance rules matter more than runtime ownership
- `responsive-design` if viewport adaptation or layout collapse is the main job
N:state-management
D:Choose the smallest viable React/fullstack state owner before comparing libraries. Use when deciding whether a request is local UI state, shared-subtree/Context state, URL or form state, server-state cache ownership, or leftover client workflow state that may justify Zustand / Redux Toolkit / Jotai.
G:state-management react context zustand redux-toolkit jotai tanstack-query server-state client-state url-state form-state react-router optimistic-updates
U[5]:
Pick one primary state packet before naming any tool
Separate URL/form/server/client-workflow ownership cleanly
Compare stores only after non-store packets are removed
Call out the tempting wrong owner explicitly
Produce a short ownership brief with route-outs
S[7]{n,action}:
1,Pick one primary packet
2,Choose the smallest owner
3,Run the anti-pattern check
4,Compare client-store options only if a client-workflow packet remains
5,Name mixed architectures directly
6,Route adjacent work out immediately
7,Produce one ownership brief
R[6]:
react-best-practices
api-design
debugging
ui-component-patterns
design-system
responsive-design