React architecture patterns, TypeScript, Next.js, hooks, and testing. Use when working with React component structure, state management, Next.js routing, Vitest, React Testing Library, or reviewing React code. For visual design and aesthetic direction, use frontend-design instead.
From compound-engineeringnpx claudepluginhub iliaal/compound-engineering-plugin --plugin compound-engineeringThis skill uses the workspace's default tool permissions.
references/e2e-testing.mdreferences/testing.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides idea refinement into designs: explores context, asks questions one-by-one, proposes approaches, presents sections for approval, writes/review specs before coding.
Verify before implementing: For App Router patterns, React 19 APIs, or version-specific behavior, search current docs via search_docs before writing code. Training data may lag current releases.
ComponentPropsWithoutRef<'button'>, add custom props via intersectionReact.ReactNode for children, React.ReactElement for single element, render prop (data: T) => ReactNode<T> with keyof T for column keys, T extends { id: string } for constraintsReact.MouseEvent<HTMLButtonElement>, FormEvent<HTMLFormElement>, ChangeEvent<HTMLInputElement>as const for custom hook tuple returnsuseRef<HTMLInputElement>(null) for DOM (use ?.), useRef<number>(0) for mutable valuesuseState<User | null>(null) for unions/null{ type: 'set'; payload: number } | { type: 'reset' }useX() hook if context is nullEffects are escape hatches -- most logic should NOT use effects.
| Need | Solution |
|---|---|
| Derived value from props/state | Calculate during render (useMemo if expensive) |
| Reset state on prop change | key prop on component |
| Respond to user event | Event handler |
| Notify parent of state change | Call onChange in event handler, or fully controlled component |
| Chain of state updates | Calculate all next state in one event handler |
| Sync with external system | Effect with cleanup |
Effect rules:
setItems(prev => [...prev, item])) to remove state dependenciesuseEffectEvent for non-reactive values (e.g., theme in a connection effect)ignore flag pattern or React QueryLocal UI state → useState, useReducer
Shared client state → Zustand (simple) | Redux Toolkit (complex)
Atomic/granular → Jotai
Server/remote data → React Query (TanStack Query)
URL state → nuqs, router search params
Form state → React Hook Form
Key patterns:
create<State>()(devtools(persist((set) => ({...})))) -- use slices for scale, selective subscriptions to prevent re-renders['users', 'detail', id] as const), staleTime/gcTime, optimistic updates with onMutate/onError rollbackCritical -- eliminate waterfalls:
Promise.all() for independent async operationsawait into branches where actually neededCritical -- bundle size:
index.ts re-exports)next/dynamic or React.lazy() for heavy componentscontent-visibility: auto + contain-intrinsic-size on long lists -- skips off-screen layout/paintRe-render optimization:
state.items.length > 0 not state.items)setCount(c => c + 1)useState(() => expensiveComputation())useTransition for non-urgent updates (search filtering)useDeferredValue for expensive derived UIcondition ? <A /> : <B />), not && for conditionalsReact.memo only for expensive subtrees with stable propsReact Compiler (React 19): auto-memoizes -- write idiomatic React, remove manual useMemo/useCallback/memo. Install babel-plugin-react-compiler, keep components pure.
forwardRef deprecated. Accept ref?: React.Ref<HTMLElement> as regular propuseFormState: const [state, formAction, isPending] = useActionState(action, initialState)const [optimistic, addOptimistic] = useOptimistic(state, mergeFn) for instant UI feedbackconst { pending } = useFormStatus() in child of <form action={...}>'use server' directive. Validate inputs (Zod), revalidateTag/revalidatePath after mutations. Server Actions are public endpoints -- always verify auth/authz inside each action, not just in middleware or layout guards<Activity mode='visible'|'hidden'> -- preserves state/DOM for toggled components (experimental)File conventions: page.tsx (route UI), layout.tsx (shared wrapper), template.tsx (re-mounted on navigation, unlike layout), loading.tsx (Suspense), error.tsx (error boundary), not-found.tsx (404), default.tsx (parallel route fallback), route.ts (API endpoint)
Rendering modes: Server Components (default) | Client ('use client') | Static (build) | Dynamic (request) | Streaming (progressive)
Decision: Server Component unless it needs hooks, event handlers, or browser APIs. Split: server parent + client child. Isolate interactive components as 'use client' leaf components -- keep server components static with no global state or event handlers.
Routing patterns:
(name) -- organize without affecting URL@slot -- independent loading states in same layout(.) -- modal overlays with full-page fallbackCaching:
fetch(url, { cache: 'force-cache' }) -- staticfetch(url, { next: { revalidate: 60 } }) -- ISRfetch(url, { cache: 'no-store' }) -- dynamicfetch(url, { next: { tags: ['products'] } }) then revalidateTag('products')Data fetching: Fetch in Server Components where data is used. Use Suspense boundaries for slow queries. React.cache() for per-request dedup. generateStaticParams for static generation. generateMetadata for dynamic SEO. Static metadata with title: { default: 'App', template: '%s | App' } for cascading page titles. after() for non-blocking side effects (logging, analytics) -- runs after response is sent. Hoist static I/O (fonts, config) to module level -- runs once, not per request.
*.test.tsx. Default for React components.renderHook + act, co-located *.test.tsgetByRole > getByLabelText > getByPlaceholderText > getByText > getByTestIdshould <behavior> when <condition>userEvent over fireEvent for realistic interactionsfindBy* for async elements, waitFor after state-triggering actionsvi.clearAllMocks() in beforeEach. Recreate state per test.
General testing discipline (anti-patterns, rationalization resistance): see writing-tests skill.
See testing patterns and examples for component, hook, and mocking examples.
See e2e testing for Playwright patterns.For Tailwind v4 configuration, utility patterns, dark mode, and component variants, see tailwind-css skill.
Class sorting in JSX: when using clsx, cva, cn, tv, or tw utility functions, keep Tailwind classes in canonical order. Configure eslint-plugin-better-tailwindcss with useSortedClasses and functions: ["clsx", "cva", "cn", "tv", "tw"] to enforce this automatically across JSX attributes and helper calls.
eslint-disable, @ts-ignore) in new codeuseEffect dependency arrays not manually overriddenforwardRef usage in React 19+ projects (use ref prop directly)