React
Full React 19 engineering, architecture, Server Components, hooks, Zustand, TanStack Query, forms, performance, testing, production deploy.
Full React 19 engineering, architecture, Server Components, hooks, Zustand, TanStack Query, forms, performance, testing, production deploy.
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Production-grade React engineering. This skill transforms how you build React applications — from component architecture to deployment.
Before writing code, make these decisions:
| Decision | Options | Default |
|---|---|---|
| Rendering | SPA / SSR / Static / Hybrid | SSR (Next.js) |
| State (server) | TanStack Query / SWR / use() | TanStack Query |
| State (client) | useState / Zustand / Jotai | Zustand if shared |
| Styling | Tailwind / CSS Modules / styled | Tailwind |
| Forms | React Hook Form + Zod / native | RHF + Zod |
Rule: Server state (API data) and client state (UI state) are DIFFERENT. Never mix them.
// ✅ The correct pattern export function UserCard({ user, onEdit }: UserCardProps) { // 1. Hooks first (always) const [isOpen, setIsOpen] = useState(false)// 2. Derived state (NO useEffect for this) const fullName =
${user.firstName} ${user.lastName}// 3. Handlers const handleEdit = useCallback(() => onEdit(user.id), [onEdit, user.id])
// 4. Early returns if (!user) return null
// 5. JSX (max 50 lines) return (...) }
| Rule | Why |
|---|---|
| Named exports only | Refactoring safety, IDE support |
| Props interface exported | Reusable, documented |
| Max 50 lines JSX | Extract if bigger |
| Max 300 lines file | Split into components |
| Hooks at top | React rules + predictable |
Is it from an API? ├─ YES → TanStack Query (NOT Redux, NOT Zustand) └─ NO → Is it shared across components? ├─ YES → Zustand (simple) or Context (if rarely changes) └─ NO → useState
// Query key factory — prevents key typos export const userKeys = { all: ['users'] as const, detail: (id: string) => [...userKeys.all, id] as const, }export function useUser(id: string) { return useQuery({ queryKey: userKeys.detail(id), queryFn: () => fetchUser(id), staleTime: 5 * 60 * 1000, // 5 min }) }
// Thin stores, one concern each export const useUIStore = create<UIState>()((set) => ({ sidebarOpen: true, toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })), }))// ALWAYS use selectors — prevents unnecessary rerenders const isOpen = useUIStore((s) => s.sidebarOpen)
// Server Component — runs on server, zero JS to client async function ProductList() { const products = await db.products.findMany() // Direct DB access return <ul>{products.map(p => <ProductCard key={p.id} product={p} />)}</ul> }// Client Component — needs 'use client' directive 'use client' function AddToCartButton({ productId }: { productId: string }) { const [loading, setLoading] = useState(false) return <button onClick={() => addToCart(productId)}>Add</button> }
| Server Component | Client Component |
|---|---|
| async/await ✅ | useState ✅ |
| Direct DB ✅ | onClick ✅ |
| No bundle size | Adds to bundle |
| useState ❌ | async ❌ |
// Read promises in render (with Suspense) function Comments({ promise }: { promise: Promise<Comment[]> }) { const comments = use(promise) // Suspends until resolved return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul> }
'use client' async function submitAction(prev: State, formData: FormData) { 'use server' // ... server logic return { success: true } }function Form() { const [state, action, pending] = useActionState(submitAction, {}) return ( <form action={action}> <input name="email" disabled={pending} /> <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button> {state.error && <p>{state.error}</p>} </form> ) }
| Priority | Technique | Impact |
|---|---|---|
| P0 | Route-based code splitting | 🔴 High |
| P0 | Image optimization (next/image) | 🔴 High |
| P1 | Virtualize long lists (tanstack-virtual) | 🟡 Medium |
| P1 | Debounce expensive operations | 🟡 Medium |
| P2 | React.memo on expensive components | 🟢 Low-Med |
| P2 | useMemo for expensive calculations | 🟢 Low-Med |
React Compiler (React 19+): Auto-memoizes. Remove manual memo/useMemo/useCallback.
// ❌ Renders "0" when count is 0 {count && <Component />}// ✅ Explicit boolean {count > 0 && <Component />}
// ❌ Mutating state — React won't detect array.push(item) setArray(array) // ✅ New reference setArray([...array, item])
// ❌ New key every render — destroys component <Item key={Math.random()} /> // ✅ Stable key <Item key={item.id} />
// ❌ useEffect cannot be async useEffect(async () => { ... }, [])// ✅ Define async inside useEffect(() => { async function load() { ... } load() }, [])
// ❌ Missing cleanup — memory leak useEffect(() => { const sub = subscribe() }, []) // ✅ Return cleanup useEffect(() => { const sub = subscribe() return () => sub.unsubscribe() }, [])
// ❌ Object in deps — triggers every render useEffect(() => { ... }, [{ id: 1 }]) // ✅ Extract primitives or memoize useEffect(() => { ... }, [id])
// ❌ Sequential fetches — slow const users = await fetchUsers() const orders = await fetchOrders()// ✅ Parallel const [users, orders] = await Promise.all([fetchUsers(), fetchOrders()])
// ❌ Race condition — no abort useEffect(() => { fetch(url).then(setData) }, [url]) // ✅ Abort controller useEffect(() => { const controller = new AbortController() fetch(url, { signal: controller.signal }).then(setData) return () => controller.abort() }, [url])
Common errors AI assistants make with React:
| Mistake | Correct Pattern |
|---|---|
| useEffect for derived state | Compute inline: |
| Redux for API data | TanStack Query for server state |
| Default exports | Named exports: |
| Index as key in dynamic lists | Stable IDs: |
| Fetching in useEffect | TanStack Query or loader patterns |
| Giant components (500+ lines) | Split at 50 lines JSX, 300 lines file |
| No error boundaries | Add at app, feature, component level |
| Ignoring TypeScript strict | Enable strict: true, fix all errors |
| Hook | Purpose |
|---|---|
| useState | Local state |
| useEffect | Side effects (subscriptions, DOM) |
| useCallback | Stable function reference |
| useMemo | Expensive calculation |
| useRef | Mutable ref, DOM access |
| use() | Read promise/context (React 19) |
| useActionState | Form action state (React 19) |
| useOptimistic | Optimistic UI (React 19) |
src/ ├── app/ # Routes (Next.js) ├── features/ # Feature modules │ └── auth/ │ ├── components/ # Feature components │ ├── hooks/ # Feature hooks │ ├── api/ # API calls │ └── index.ts # Public exports ├── shared/ # Cross-feature │ ├── components/ui/ # Button, Input, etc. │ └── hooks/ # useDebounce, etc. └── providers/ # Context providers
See
setup.md for first-time configuration. Uses memory-template.md for project tracking.
export function X not export default. Enables safe refactoring.const total = items.reduce(...). Effects are for side effects.item.id, never index for dynamic lists.any, no implicit nulls. Catch bugs at compile time.Install with
clawhub install <slug> if user confirms:
clawhub star reactclawhub syncNo automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.