state-management — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited state-management (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
What it is: State management is the frontend architecture discipline of deciding where each changing value lives, who owns it, how it propagates to consumers, and how it stays consistent over time.
Mental model: State is at least four kinds with different owners and lifetimes: server state, client UI state, URL state, and persistent state. Classify the value first, then choose the narrowest owner and tool that fits its lifetime and invalidation needs.
Why it exists: Most recurring frontend state bugs come from putting the right value in the wrong place: server data in a general-purpose store, URL-worthy state in component state, duplicated values in two owners, or global stores used before local state has proven insufficient.
What it is NOT: It is not tactical state-library selection, API schema design, finite-state workflow modeling, distributed replication, or the client/server execution-boundary decision. Those skills can compose with this one after the state kind and owner are clear.
Adjacent concepts: Rendering models, client-server boundary, frontend architecture, API design, state-machine modeling, server-state caching, and URL state.
One-line analogy: State management is like addressing mail: each item needs the right destination for its kind, and one mailbox for everything creates predictable delivery failures.
Common misconception: The wrong starting question is "which state library should hold this?" The right first question is "what kind of state is this, who owns it, and how long should it live?"
The architectural discipline of deciding, for each distinct piece of data an application reads or writes, where it lives, who owns it, how it propagates, and how it stays consistent. Covers the four kinds of state (server, client UI, URL, persistent), the colocation default and the lifting move, the single-source-of-truth principle, the React Query / SWR doctrine for server state, the URL as a state container, the architectural anti-patterns of premature globalization and state sprawl, optimistic-update trade-offs, and the framing of state ownership as a design contract distinct from state-library selection.
State management is a series of location and ownership decisions, each of which has a correct default. The defaults are: colocate locally; lift only when needed; put server state in a server-state library; put URL-worthy state in the URL; put session-survival state in persistent storage; never have two sources of truth for one value. Most recurring frontend bugs (broken back-button, stale data, prop drilling, "why is this re-rendering," changes that don't reflect everywhere) are violations of these defaults. The discipline is not the library; it is the application of the defaults.
The discipline matters because state is not one thing — server state, client UI state, URL state, and persistent state have different lifetimes, invalidation rules, and consistency requirements. Treating them as one category (one store, one set of patterns) produces a codebase that gets all four kinds of state slightly wrong simultaneously. Treating them as four categories produces a codebase where each kind is handled by the tool best suited to it.
For agents writing or reviewing frontend code, the discipline is the framework that lets the agent reason locally — pick up a piece of state, classify it (server / client / URL / persistent), check its current location against the right location for its kind, and make targeted fixes. Without the framework, the agent pattern-matches against whatever the codebase already does, replicating existing choices without questioning whether they were correct.
| Kind | Owned by | Lifetime | Invalidation rule | Common tools |
|---|---|---|---|---|
| Server state | A backend | Session, with revalidation | Time-based (stale-while-revalidate); event-based (mutation, focus, reconnect); manual | React Query, SWR, RTK Query, Apollo, Server Components |
| Client UI state | A component or hook in the active session | Ephemeral within a render lifecycle | Recreated on unmount | useState, useReducer, Context, Zustand, Jotai |
| URL state | The URL itself; framework router | Until URL changes | URL navigation | useSearchParams, route params, nuqs |
| Persistent state | The browser's local storage | Until cleared | Manual via app code | localStorage, IndexedDB, cookies |
Each piece of state in an application falls into one of these. Misclassifying produces predictable bugs — server data in a general-purpose store loses automatic revalidation; URL-worthy state in component state breaks the back-button; persistent state in component state loses on refresh.
"State should live as close as possible to where it's used." — colocation, deliberate lifting, escalation only by need.
The decision procedure:
useState or useReducer.useContextSelector or pattern-match to slice if many consumers depend on different parts of a large value.Premature jumps (skipping step 2 to land at step 4, or starting at step 4 by default) cause state sprawl. The cost of climbing the ladder later (refactoring local to lifted) is less than the cost of being too high up to start.
Server state has four properties that no general-purpose store handles by default:
| Property | What it requires | Why a server-state library wins |
|---|---|---|
| Remote canonical source | The client cache may diverge from the server | Built-in stale-while-revalidate, refetch on focus/reconnect |
| Deduplication | Two components asking for the same data should fetch once | Query-key cache prevents duplicate fetches |
| Staleness | Old data should be replaced | Time-based and event-based invalidation |
| Mutation-triggered invalidation | Updating data should refresh related queries | Mutate-then-invalidate pattern by query key |
The recurring failure: using Redux/Zustand for everything, then manually building "refetch on focus" middleware, manual deduplication, manual cache expiry, and manual mutation-invalidation rules. Each one of those is correct in concept; together they reimplement React Query, badly. The disciplined answer is to use a server-state library for server data and keep the general-purpose store (if any) for genuinely client UI state.
"Should two people opening this URL see the same view?"
If yes, the state belongs in the URL. If no, it doesn't.
| State | URL-worthy? | Why |
|---|---|---|
| Current filter, sort, page | Yes | Sharing a link reproduces the view; back-button works |
| Selected tab in a multi-tab panel | Often yes | Bookmark-able sub-view |
| Open/closed of a sidebar | Usually no | Personal preference, not the shared view |
| Modal open/closed | Sometimes | If it's a deep-linkable modal (sharable URL), yes; if it's transient confirmation, no |
| Form values mid-edit | No | Ephemeral; shouldn't survive refresh from a stranger's link |
| Pagination cursor | Yes | Restores state on refresh; shareable position |
| Search query | Yes | Canonical "what is this user looking for" |
| Currently-hovered element | No | Ephemeral; not part of the shareable view |
| Anti-pattern | Symptom | Fix |
|---|---|---|
| Premature globalization | Every new state goes into the global store; the store grows; nobody knows what's in it | Start local. Only lift on observed need. Audit the store for entries that haven't been read in N months. |
| Server data in a general-purpose store | Custom middleware for refetch, dedup, invalidation; bugs in each | Migrate server data to React Query/SWR/RTK Query. Keep the general store for UI state only. |
| URL-worthy state in component state | Back-button doesn't work; refresh loses filters; can't share a link | Move to useSearchParams or equivalent. Adopt nuqs for type-safe URL state. |
| Prop drilling through 7 layers | Tedious to maintain; refactors break | Context for tree-shared data; or composition (children-as-prop) to avoid intermediate awareness. |
| Duplicated state (same value, two locations) | Updates in one place don't reflect in the other | Identify the canonical owner; derive in the other location or sync explicitly. |
| Stored derived state without need | Caches go stale; bugs from "I updated X but Y still shows old" | Compute derived state at read time. Use useMemo only with measured perf evidence. |
| Optimistic updates for high-failure mutations | "Did that work? Oh no, it didn't" UX | Use pessimistic (loading then success/error) for low-confidence mutations; optimistic for high-confidence only. |
| localStorage for everything user-prefs | Server doesn't know on first render; no cross-device sync | Server-side store for prefs that affect SSR; URL state for session-scoped prefs; localStorage only when local-only is correct. |
After applying this skill, verify:
| Instead of this skill | Use | Why |
|---|---|---|
| Choosing between Redux, Zustand, Jotai, Recoil, MobX, etc. | Library docs + framework conventions | Library choice is a tactical decision below this skill |
| Designing the JSON shape of an API endpoint | api-design | api-design owns the external request/response surface; this skill owns where the response lives once it arrives |
| Modeling the finite-state transitions of a workflow | state-machine-modeling | state-machine-modeling owns the workflow representation; this skill owns the location decision |
| Encoding/decoding URL search params, route params, hash | url-state-management | url-state-management owns the encoding patterns; this skill owns the upstream "should it be in the URL" decision |
| Building distributed state across services | replication-patterns | replication-patterns owns multi-replica consistency; this skill applies to single-client state |
| Deciding what runs on server vs client | client-server-boundary | client-server-boundary owns the code-location boundary; this skill owns the state-location decision orthogonal to it |
| Building the cache infrastructure of a server-state library | the library itself (React Query docs, etc.) | The library is the implementation of what this skill recommends; don't reimplement |
| Concurrency control across multiple users editing the same record | (no direct skill — out of scope) | Conflict resolution mechanisms (versioning, OT, CRDT) are a separate concern |
<!-- skill-graph-context:start (generated — do not edit by hand) -->
Classification
frontend-engineeringtrueengineering/frontendWhen to use
where should this state live, should this be in component state or global, I have state across multiple routes, this prop is drilled through 5 components, is this server state or client state, should this be in the URLNot for
client-server-boundary: the line between code-that-runs-where (server components, client components, the serialization boundary)rendering-models: the question ofRelated skills
rendering-models, api-design, state-machine-modelingrendering-models, client-server-boundary, frontend-architecture, api-design, state-machine-modelingConcept
Keywords
state management, state colocation, lifting state, state derivation, single source of truth, server state, client state, URL state, persistent state, ephemeral state<!-- skill-graph-context:end -->
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.