React Boundaries
Own state, data, and side effects at the lowest correct boundary. Parents compose structure; they do not become data hubs.
Principle: a component should own what it needs to render and act—unless another boundary must genuinely coordinate, gate, or load in parallel.
Hard Defaults
- Push down. State and fetches live with the consumer. Lift only via the exception list.
- Pass identity, not payloads. Give children an
id (or stable handle); let them subscribe or query. Do not drill entities through wrappers that never use them.
- Compose UI, don’t pipe props. Prefer
children / slots / named parts over threading data, isLoading, and handlers through layout shells.
- Shared clients, local hooks. One API or QueryClient; many leaf hooks. Do not centralize “call the API” five layers up.
- Structure over memo theater. Fix ownership and tree shape before spraying
memo / useCallback / useMemo. Prefer patterns that work with React Compiler.
- Justify every hoist in one line when you lift or route-fetch (“SEO”, “gate before mount”, “sibling sync”, “anti-waterfall”).
When Hoisting Is Correct
Hoist state or data ownership only for:
| Exception |
Own at |
| Route / SSR / SEO needs data before paint or for meta |
Router loader, Start server function, or RSC prefetch |
| Auth, permissions, or “don’t mount until X” |
Route or parent gate |
| Two+ siblings must share one interactive source of truth |
Nearest common parent or a narrow store |
| Parent must know which children exist before they can fetch |
Parent / loader (anti-waterfall) |
| Cache already dedupes the same key |
Leaves still subscribe; parent may prefetch only |
Everything else stays down. Details: ownership.md.
Workflow
- Scope — feature, route, or component tree the user named.
- Map ownership — for each piece of state and each query/mutation, name the owner and who re-renders when it changes.
- Apply defaults — move ownership down; replace drilled payloads with ids + leaf subscriptions; replace prop pipelines with composition.
- Apply TanStack patterns — tanstack-patterns.md.
- Check composition — composition.md.
- Kill anti-patterns — anti-patterns.md.
- Verify — typecheck/lint/tests for the touched surface; confirm high-churn state no longer sits in page/route shells.
Placement Checklist
Before finishing a change, answer:
Non-Goals
- Not “never use Context,” “never use route loaders,” or “never pass props.”
- Not restyling, a11y primitive design, or general React API docs.
- Not replacing TanStack Query with ad-hoc parent
useEffect fetching.
Completion
Report briefly:
- Ownership moves (what moved down or stayed up, and why).
- Prop-drilling or data-hub removals.
- Prefetch vs subscribe split (if any).
- Remaining hoists with their exception justification.
1---2name: react-boundaries3description: Enforces React component ownership boundaries—colocate state, queries, and mutations with consuming leaves; avoid prop drilling and parent data hubs; structure trees for render isolation and composability. Includes TanStack Query, Router, Start, Form, and Store patterns. Use when designing or reviewing component architecture, refactoring prop-heavy trees, fixing unnecessary re-renders from lifted state, deciding where API or query logic should live, or when the user says react-boundaries, ownership boundaries, push state down, or stop prop drilling.4---56# React Boundaries78Own state, data, and side effects at the **lowest correct boundary**. Parents compose structure; they do not become data hubs.910**Principle:** a component should own what it needs to render and act—unless another boundary must genuinely coordinate, gate, or load in parallel.1112## Hard Defaults13141. **Push down.** State and fetches live with the consumer. Lift only via the exception list.152. **Pass identity, not payloads.** Give children an `id` (or stable handle); let them subscribe or query. Do not drill entities through wrappers that never use them.163. **Compose UI, don’t pipe props.** Prefer `children` / slots / named parts over threading `data`, `isLoading`, and handlers through layout shells.174. **Shared clients, local hooks.** One API or QueryClient; many leaf hooks. Do not centralize “call the API” five layers up.185. **Structure over memo theater.** Fix ownership and tree shape before spraying `memo` / `useCallback` / `useMemo`. Prefer patterns that work with React Compiler.196. **Justify every hoist** in one line when you lift or route-fetch (“SEO”, “gate before mount”, “sibling sync”, “anti-waterfall”).2021## When Hoisting Is Correct2223Hoist state or data ownership only for:2425| Exception | Own at |26| --- | --- |27| Route / SSR / SEO needs data before paint or for meta | Router loader, Start server function, or RSC prefetch |28| Auth, permissions, or “don’t mount until X” | Route or parent gate |29| Two+ siblings must share one interactive source of truth | Nearest common parent or a narrow store |30| Parent must know *which* children exist before they can fetch | Parent / loader (anti-waterfall) |31| Cache already dedupes the same key | Leaves still subscribe; parent may **prefetch only** |3233Everything else stays down. Details: [ownership.md](references/ownership.md).3435## Workflow36371. **Scope** — feature, route, or component tree the user named.382. **Map ownership** — for each piece of state and each query/mutation, name the owner and who re-renders when it changes.393. **Apply defaults** — move ownership down; replace drilled payloads with ids + leaf subscriptions; replace prop pipelines with composition.404. **Apply TanStack patterns** — [tanstack-patterns.md](references/tanstack-patterns.md).415. **Check composition** — [composition.md](references/composition.md).426. **Kill anti-patterns** — [anti-patterns.md](references/anti-patterns.md).437. **Verify** — typecheck/lint/tests for the touched surface; confirm high-churn state no longer sits in page/route shells.4445## Placement Checklist4647Before finishing a change, answer:4849- [ ] Who re-renders if this state or query updates?50- [ ] Who actually *uses* this data or state?51- [ ] Can the leaf own the query/mutation (or subscribe to a shared key)?52- [ ] Are intermediates composing UI or only forwarding props?53- [ ] If hoisted: which exception applies (one-line justification)?54- [ ] Is Context / Store carrying stable deps or high-churn values that should be local?5556## Non-Goals5758- Not “never use Context,” “never use route loaders,” or “never pass props.”59- Not restyling, a11y primitive design, or general React API docs.60- Not replacing TanStack Query with ad-hoc parent `useEffect` fetching.6162## Completion6364Report briefly:6566- Ownership moves (what moved down or stayed up, and why).67- Prop-drilling or data-hub removals.68- Prefetch vs subscribe split (if any).69- Remaining hoists with their exception justification.