---
name: building-react-components
description: Designs and writes React components with clean props APIs, composition over configuration, correct state placement, and hooks-based logic reuse. Use when the user asks to create, refactor, or review a React component, design a component API, decide where state should live, extract a custom hook, or fix prop drilling or unnecessary re-renders. Do not use for CSS layout mechanics (designing-responsive-layouts) or non-React frameworks — adapt the principles manually there.
---
# Building React Components
## When to use / when NOT to use
- **Use for:** creating or refactoring React components, designing props APIs, placing state, extracting custom hooks, reviewing component structure.
- **Do NOT use for:** CSS layout/breakpoint work (designing-responsive-layouts), styling systems (theming-design-tokens), Vue/Svelte/Angular components, or backend data modeling.
## Core rules
1. **Narrow, typed props — no boolean explosions.** One `variant` union beats three flags that can contradict each other.
- ✅ `variant: 'primary' | 'danger' | 'ghost'`
- ❌ `isPrimary`, `isDanger`, `isGhost` as three separate booleans
2. **Composition over configuration.** When a component grows a prop per content slot, switch to `children` or slot components.
- ✅ `……`
- ❌ ``
3. **State lives at the lowest component that needs it; lift only when shared.** Before `useState`, pick the state's home: server data → query library cache; shareable/bookmarkable → URL; everything else → local state closest to use.
- ✅ search text in the `SearchBox`, results in the query cache, filters in the URL
- ❌ every field of a page hoisted into one context "to be safe"
4. **Pick controlled or uncontrolled per input and stay consistent.** Controlled (`value` + `onChange`) when other UI reacts per keystroke; uncontrolled (`defaultValue` + read on submit) for plain forms.
- ❌ `value` without `onChange`, or switching between the two mid-lifecycle
5. **Extract reusable logic into custom hooks, not wrapper components.** A hook named `useX` returning plain values beats render-props or HOC indirection.
- ✅ `const { data, error } = usePolling(url, 5000)`
- ❌ ` …} />`
6. **Memoize only after measuring.** No `React.memo`/`useMemo`/`useCallback` by default; add them when the Profiler shows a real re-render cost, and comment why.
- ❌ wrapping every callback in `useCallback` "for performance"
7. **One component per file, named exports, file named after the component.** `UserMenu.tsx` exports `UserMenu`; its private subcomponents stay in the same file until reused elsewhere.
8. **Derive, don't sync.** Values computable from existing props/state are computed during render — never mirrored into state with an effect.
- ✅ `const fullName = first + ' ' + last`
- ❌ `useEffect(() => setFullName(first + ' ' + last), [first, last])`
## Workflow
1. Name the component and write its props type first — if the type needs more than ~7 props or any boolean pair, redesign with rules 1–2.
2. Decide each piece of state's home (rule 3) before writing any `useState`.
3. Implement render logic; derive values instead of syncing state (rule 8).
4. Extract any logic used twice into a custom hook (rule 5).
5. Self-review: re-check every prop against rules 1–2, every `useState` against rules 3 and 8, every memoization against rule 6. Fix violations before delivering.
## Edge cases & failure modes
- **Existing codebase conventions conflict with these rules** → match the codebase; note the divergence in one sentence, don't refactor uninvited.
- **Class components in the file being edited** → keep the class style for small edits; propose (don't perform) a hooks migration.
- **Prop drilling more than 2 levels** → prefer composition (pass the composed element down) before reaching for context.
- **Server components (Next.js/RSC)** → hooks and state are client-only; add `'use client'` only at the interactive leaf, not the page root.
## References
Copy-paste patterns and gotchas: see [references/patterns.md](references/patterns.md)