Install for your agent
Download the file and place it at the path for your coding agent. Review the instructions before enabling a third-party skill.
Codex
~/.codex/skills/state-boundary-architecture-review/SKILL.mdClaude Code
.claude/skills/state-boundary-architecture-review/SKILL.mdCursor
.cursor/skills/state-boundary-architecture-review/SKILL.mdGitHub Copilot
.github/skills/state-boundary-architecture-review/SKILL.md</>View raw SKILL.mdInspect only the executable instructions your coding agent will receive, without catalog metadata.
---
name: "state-boundary-architecture-review"
description: "Classify React state as local, server, URL, or global client state, establish a source of truth, and remove duplicated ownership without introducing unnecessary stores."
---
# Review State Boundaries in React Architecture
Use this skill when the same data is mirrored across stores, hooks, query caches, URLs, or components; synchronization Effects are multiplying; or the source of truth is unclear.
The goal is not to choose a favorite state library. Classify ownership first, then keep the smallest mechanism that matches the data lifecycle.
## State ownership model
```text
Local UI state → nearest owning component
Server state → cache/data layer
URL state → router/search params
Global client state → truly application-wide browser state
Derived state → compute it instead of storing it
```
These are decision categories, not rigid folder rules. Lifecycle and authority decide ownership.
## Workflow
### 1. Build a state inventory
```text
State:
Source of truth:
Readers:
Writers:
Lifetime:
Must survive navigation?
Must be shareable in URL?
Comes from server?
```
### 2. Detect duplicate authority
Prioritize cases where query results are copied into Redux or Zustand, search params are mirrored in `useState`, derived values are synchronized by Effects, parent and child both own editable copies, or persistence is enabled without a product requirement.
### 3. Separate server and client state
Data whose authoritative source is an API normally belongs in the server-state layer:
```text
API
↓
Query cache
↓
UI
```
Do not move modal visibility, a local selected tab, or an unsaved form draft into a query cache simply because multiple components can see it.
### 4. Use the URL for navigational state
Filters, sorting, pagination, and tabs are strong URL candidates when refresh, browser navigation, or link sharing should preserve them. Avoid maintaining a second local mirror unless the product explicitly needs an unapplied draft.
### 5. Compute derived values
```tsx
// avoid
const [fullName, setFullName] = useState("")
useEffect(() => {
setFullName(`${firstName} ${lastName}`)
}, [firstName, lastName])
// prefer
const fullName = `${firstName} ${lastName}`
```
### 6. Make global client state the last option
Before moving data there, ask whether it can stay local, already belongs to server state, belongs in the URL, or only needs a subtree-scoped context.
### 7. Remove synchronization before adding abstractions
Audit Effects whose only purpose is keeping two representations aligned. Deleting duplicate state often solves the architecture problem more directly than adding selectors, middleware, or another state library.
## Expected output
```text
State:
Current owner:
Problem:
Recommended owner:
Migration:
Risk:
Validation:
```
Do not migrate state merely to standardize tooling. The desired outcome is fewer sources of truth, less manual synchronization, and clearer ownership.
After refactoring, validate refresh behavior, navigation and back/forward, loading and error paths, relevant optimistic mutations, and the repository's existing checks.
Use this skill when the same data is mirrored across stores, hooks, query caches, URLs, or components; synchronization Effects are multiplying; or the source of truth is unclear.
The goal is not to choose a favorite state library. Classify ownership first, then keep the smallest mechanism that matches the data lifecycle.
State ownership model
Local UI state → nearest owning component
Server state → cache/data layer
URL state → router/search params
Global client state → truly application-wide browser state
Derived state → compute it instead of storing it
These are decision categories, not rigid folder rules. Lifecycle and authority decide ownership.
Workflow
1. Build a state inventory
State:
Source of truth:
Readers:
Writers:
Lifetime:
Must survive navigation?
Must be shareable in URL?
Comes from server?
2. Detect duplicate authority
Prioritize cases where query results are copied into Redux or Zustand, search params are mirrored in useState, derived values are synchronized by Effects, parent and child both own editable copies, or persistence is enabled without a product requirement.
3. Separate server and client state
Data whose authoritative source is an API normally belongs in the server-state layer:
API
↓
Query cache
↓
UI
Do not move modal visibility, a local selected tab, or an unsaved form draft into a query cache simply because multiple components can see it.
4. Use the URL for navigational state
Filters, sorting, pagination, and tabs are strong URL candidates when refresh, browser navigation, or link sharing should preserve them. Avoid maintaining a second local mirror unless the product explicitly needs an unapplied draft.
5. Compute derived values
// avoid
const [fullName, setFullName] = useState("")
useEffect(() => {
setFullName(`${firstName} ${lastName}`)
}, [firstName, lastName])
// prefer
const fullName = `${firstName} ${lastName}`
6. Make global client state the last option
Before moving data there, ask whether it can stay local, already belongs to server state, belongs in the URL, or only needs a subtree-scoped context.
7. Remove synchronization before adding abstractions
Audit Effects whose only purpose is keeping two representations aligned. Deleting duplicate state often solves the architecture problem more directly than adding selectors, middleware, or another state library.
Expected output
State:
Current owner:
Problem:
Recommended owner:
Migration:
Risk:
Validation:
Do not migrate state merely to standardize tooling. The desired outcome is fewer sources of truth, less manual synchronization, and clearer ownership.
After refactoring, validate refresh behavior, navigation and back/forward, loading and error paths, relevant optimistic mutations, and the repository's existing checks.