
React Patterns
Ship React 18/19 UI with disciplined hooks, RSC boundaries, and accessible composition without effect-heavy anti-patterns.
Install
npx skills add https://github.com/affaan-m/ecc --skill react-patternsWhat is this skill?
- Hooks discipline, derived state during render, and migration away from class/forwardRef-heavy legacy patterns
- Server vs Client Component boundaries for Next.js App Router and RSC data-fetching choices
- Suspense, error boundaries, React 19 form actions, and TanStack Query / SWR integration guidance
- State decision trees: local, lifted, context, and external stores without redundant useEffect sync
- Accessibility-first composition baked into idiomatic component design
Adoption & trust: 1 installs on skills.sh; 211k GitHub stars; trending (+100% hot-view momentum).
Recommended Skills
Frontend Designanthropics/skills
Vercel React Best Practicesvercel-labs/agent-skills
Remotion Best Practicesremotion-dev/skills
Vercel Composition Patternsvercel-labs/agent-skills
Develop Userscriptsxixu-me/skills
Next Best Practicesvercel-labs/next-skills
Journey fit
Primary fit
React component and hook work sits in Build; the skill is shelved on frontend as the default home for JSX/TSX implementation. Patterns target UI trees, forms, data fetching in the browser/RSC split—core frontend craft for solo SaaS and marketing sites.
SKILL.md
READMESKILL.md - React Patterns
# React Patterns Idiomatic React 18/19 patterns for building robust, accessible, performant component trees. ## When to Activate - Writing or modifying React function components, custom hooks, or component trees - Reviewing JSX/TSX files - Designing state shape or component composition - Migrating class components or older `forwardRef`/`useEffect`-heavy code - Choosing between local state, lifted state, context, and external stores - Working with Server Components / Client Components (Next.js App Router, RSC) - Implementing forms with React 19 actions or controlled inputs - Wiring data fetching with TanStack Query / SWR / RSC ## Core Principles ### 1. Render is a Pure Function of Props and State ```tsx // Good: derive during render function Cart({ items }: { items: CartItem[] }) { const total = items.reduce((sum, i) => sum + i.price * i.qty, 0); return <span>{formatMoney(total)}</span>; } // Bad: derived state stored separately function Cart({ items }: { items: CartItem[] }) { const [total, setTotal] = useState(0); useEffect(() => { setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0)); }, [items]); return <span>{formatMoney(total)}</span>; } ``` Derived state in `useEffect` adds a render cycle, can desync, and obscures the data flow. ### 2. Side Effects Outside Render Effects, mutations, network calls, and subscriptions live in event handlers or `useEffect` — never in the render body. ### 3. Composition Over Inheritance React has no inheritance model for components. Compose with `children`, render props, or component props. ## Hooks Discipline See [rules/react/hooks.md](../../rules/react/hooks.md) for the full ruleset. Highlights: - Top-level only, never conditional - Cleanup every subscription, interval, listener - Functional updater (`setX(prev => prev + 1)`) when new state depends on old - Default position: do not memoize — add `useMemo`/`useCallback` only when a profiler or a dependency chain proves it matters - Extract a custom hook only when the same hook sequence appears in 2+ components ## State Location Decision Tree ``` Used by one component? -> useState inside it Used by parent + a few descendants? -> lift to nearest common ancestor Used across distant branches AND low-frequency reads (theme, auth, locale)? -> React Context High-frequency updates shared across the tree? -> external store (Zustand, Jotai, Redux Toolkit) Derived from a server? -> server-state library (TanStack Query, SWR, RSC fetch) ``` Most pages do not need context or a global store. Resist abstraction until duplicated lifting becomes painful. ## Server / Client Components (RSC) ```tsx // Server Component - default, async, never ships JS for itself export default async function ProductPage({ params }: { params: { id: string } }) { const product = await db.product.findUnique({ where: { id: params.id } }); if (!product) notFound(); return <ProductView product={product} />; } // Client Component - opt in with "use client" "use client"; export function AddToCartButton({ productId }: { productId: string }) { const [pending, startTransition] = useTransition(); return ( <button disabled={pending} onClick={() => startTransition(() => addToCart(productId))} > {pending ? "Adding..." : "Add to cart"} </button> ); } ``` Boundaries: - Server -> Client: pass serializable props or `children` - Client -> Server: invoke Server Actions via `<form action={...}>` or imperatively from event handlers - Never `import` a Server Component from a Client Component file — compose them via `children` instead ## Suspense + Error Boundaries ```tsx <ErrorBoundary fall