| name | react-patterns |
| description | Guides correct React implementation and component design. Use this when implementing React components, hooks, or features — read the relevant React 18 docs first, then apply the checklist before writing code. Also use this when the same pattern appears in multiple places and needs to be extracted into a shared utility or component. |
React Patterns
Mandatory: Read the docs before implementing
Before writing any React feature, open and read the relevant page from the official React 18 docs at https://18.react.dev/learn. This is non-negotiable. The docs are the single source of truth for correct React patterns.
Quick-reference map — match your task to the doc:
Implementation checklist
Work through this before and during writing any React code.
1. Component design
2. State
3. Effects
4. Lists and keys
5. Refs
6. Context
7. Custom hooks
When to extract shared utilities or components
Apply the rule of two: if the same pattern appears in 2 or more places, extract it before writing it a third time. Do not over-abstract on first use — write it inline, extract on second occurrence.
Decision table
| What is repeating | Extract to |
|---|
| UI component used across 2+ dashboard pages | packages/ui/src/ (web-only; requires Tailwind) |
| TanStack Query hook with same shape | hooks/ directory inside the app |
| Pure calculation or data transformation | packages/lib/src/ — must be pure, no I/O, no side effects |
| Zod schema reused across 2+ forms | packages/types/src/models.ts |
| Mobile UI component used in 2+ screens | apps/mobile/components/ |
| Context + hook pair used in 2+ places | Dedicated contexts/ or providers/ directory in the app |
Extraction checklist
Before extracting, confirm:
Common anti-patterns to reject
| Anti-pattern | Correct approach |
|---|
useEffect to sync derived state | Compute the derived value during render |
useEffect to respond to a user event | Move logic into the event handler |
| Array index as list key | Use stable DB id |
Mutating state directly (state.x = y) | Replace with setter: setState({ ...state, x: y }) |
| Reading a ref during render | Use state instead if the value affects output |
| Prop drilling 3+ levels | Lift to context with a custom hook |
| Same hook logic copy-pasted in 2+ files | Extract to a custom useX hook |
| Same UI pattern copy-pasted in 2+ files | Extract to a shared component |
any type on props or state | Use explicit types; narrow with type guards |
getSession() in Supabase calls | Always use getUser() |