React component discipline: pure components, minimal state, effects as escape hatches. Invoke whenever task involves any interaction with React code — writing, reviewing, refactoring, debugging, or understanding JSX, hooks, component architecture, state management, or performance optimization.
Applies React best practices for component purity, minimal state, and proper hook usage during code creation and review.
npx claudepluginhub xobotyi/cc-foundryThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/components.mdreferences/hooks.mdreferences/performance.mdreferences/state.mdreferences/testing.mdComponents are pure functions. State is minimal. Effects are escape hatches. If you reach for useEffect, verify you actually need it.
React rewards thinking in components: break UI into pieces, find minimal state, identify where it lives, and wire data flow from parent to child. References contain extended examples, rationale, and edge cases for each topic area.
| Topic | Reference | Contents |
|---|---|---|
| Components | references/components.md | Composition, refs, metadata, custom elements |
| Hooks | references/hooks.md | Hook rules, custom hooks, useSyncExternalStore |
| State | references/state.md | Placement, reducers, context, actions |
| Performance | references/performance.md | Compiler, memoization, server components, streaming |
| Testing | references/testing.md | Query priority/variants, userEvent catalog, async patterns |
Build UI in five steps:
React assumes every component is a pure function. Same props + same state = same JSX. Never mutate props, state, or variables declared before rendering.
| Side effect type | Where it belongs |
|---|---|
| User clicks, form submits | Event handlers |
| Sync with external system (DOM, network) | useEffect (last resort) |
| Data transformation | Compute during render |
| Shared logic between handlers | Extract a function, call from handlers |
React.FC — it adds implicit children typing and complicates generics.
Use function Component(props: Props).Separate logic from rendering. The component body handles computation, state, and handler definitions. JSX is declarative — it references results, not processes.
handle object. This creates
a clear boundary between logic and rendering:
const handle = {
submit() { /* ... */ },
inputChange(e: ChangeEvent<HTMLInputElement>) { setName(e.target.value); },
keyDown(e: KeyboardEvent) { if (e.key === 'Enter') handle.submit(); },
};
Reference in JSX as onChange={handle.inputChange}. Never inline handler logic in JSX.const tabElements: ReactNode[] = [];
for (const tab of allTabs) {
tabElements.push(<Tab key={tab.id}>{tab.name}</Tab>);
}
return <TabList>{tabElements}</TabList>;
JSX .map() inside the return statement is discouraged — compute element arrays in the
body, reference them in JSX.{isVisible && <Component />}) are
acceptable inline in JSX. When the condition is complex or involves multiple branches,
compute the result in the component body and reference the variable in JSX.children or render props instead of
building components with dozens of boolean flags.children props
haven't changed, so children skip re-rendering.ref is a prop. Pass ref directly as a prop to function components.
Never use forwardRef — it is deprecated.{} not parentheses to
prevent TypeScript confusion.Render <title>, <meta>, <link> directly in components. React hoists them to
<head> automatically. Works with client-only apps, streaming SSR, and Server Components.
React provides full custom element support. Server rendering: primitive props render as attributes, non-primitive props are omitted. Client rendering: props matching element instance properties are assigned as properties, others as attributes.
<Input />.<Input disabled /> not disabled={true}.<>...</> or <Fragment key={id}>.&& with numbers — count && <List /> renders 0. Use count > 0 && <List />
or a ternary.handle object in the
component body (see Component Body Organization).{...props} makes it unclear what a component accepts.
Prefer explicit props.key on every list item. Stable, unique identifiers. Never use array index as key
when items can reorder.use() — Context and Promisesuse(MyContext) over useContext(MyContext). use() can be called inside
conditionals and loops — useContext() cannot.use() always looks for the closest provider above the calling component.use(promise) integrates with Suspense and Error Boundaries to read promise values.async/await over use().use() cannot be called in a try-catch block. Use Error Boundaries or
promise.catch() instead.use() can be called conditionally.Use Effects only to synchronize with external systems (DOM APIs, network, browser events). Not for transforming data, handling user events, or state derivation.
You don't need an Effect for:
| Situation | Do this instead |
|---|---|
| Transform data for rendering | Compute during render |
| Handle user events | Call in event handler |
| Reset state on prop change | Use key={userId} on the component |
| Adjust state on prop change | Compute: items.find(...) during render |
| Notify parent of state change | Call onChange in the event handler |
| Share logic between handlers | Extract a function, call from both handlers |
| Chain state updates | Calculate all state in one event handler |
You DO need an Effect for:
Every Effect that subscribes must return a cleanup function. Data fetching in Effects
must use a cleanup flag (let ignore = false) to prevent race conditions. Prefer a
data-fetching library or use() with Suspense over raw Effects for fetching.
use followed by a capital letter. Functions that don't call hooks
should NOT start with use.useOnlineStatus not useMount.useState into a hook — that's unnecessary abstraction.Return value conventions:
return isOnline)return [value, setValue] as const)return { value, onChange, reset })useSyncExternalStoreFor subscribing to external data stores, prefer useSyncExternalStore over manual
Effect + state. Provide a subscribe function, a client snapshot getter, and a server
snapshot getter for SSR.
useState.children.use().useState + useEffect.Local state → Lift state up → Composition → Context → External library
| Category | Examples | Tool |
|---|---|---|
| UI state | Modal open, form input, selected tab | useState, useReducer, Context |
| Server cache | User data, search results, API responses | react-query, SWR, framework loaders |
Never reinvent caching, deduplication, and race condition handling with raw useState.
useStatesetCount(prev => prev + 1) not setCount(count + 1).useState(() => createInitialState()) not useState(createInitialState()).useReducerUse when state updates are complex — many event handlers modifying the same state, or when next state depends on previous state in non-trivial ways.
| Signal | Tool |
|---|---|
| Single value, simple updates | useState |
| Multiple related values, complex transitions | useReducer |
| Many event handlers doing similar state updates | useReducer |
| Need to test state logic in isolation | useReducer |
Reducer rules:
reset_form not five
separate set_field actions.'added_task' not 'set_tasks'.default case that throws to catch typos early.as const for action types in TypeScript.key for Identity ResetUse key to reset a component's state when the conceptual entity changes:
<Profile key={userId} />. This is cleaner than using an Effect to reset state on
prop change.
<Context value={...}> directly — not <Context.Provider value={...}>.createContext() calls are independent — they don't override each other.useReducer with context. Split into two contexts
(data + dispatch) so components that only dispatch don't re-render on data changes.useActionState for form submissions and data mutations. It manages pending state,
errors, and sequential action queuing automatically.<form action>, React wraps submission in a transition automatically.
When calling dispatch manually, wrap in startTransition.useOptimistic for instant UI feedback while async Actions complete. The optimistic
state reverts to real value when the Action completes or fails.useOptimistic for complex updates (e.g., adding to a list).useFormStatus reads submission status of the nearest parent <form> — must be
called from a component rendered inside a <form>, not in the same component.ButtonProps).
Never define prop types inline in the function signature. Use
function Button(props: ButtonProps) or destructure:
function Button({ label }: ButtonProps).React.FC. Use plain function declarations.(e: React.ChangeEvent<HTMLInputElement>) => void.as const for action types in reducers.React Compiler is a build-time tool that automatically applies memo, useMemo, and
useCallback equivalents. When using the compiler:
memo, use useMemo, or useCallback in new code.memo, useMemo, useCallback.Fix the slow render before you fix the re-render. Restructuring beats memoization.
memo(Component) — skip re-rendering when props unchanged. Useful when: component
re-renders often with same props, re-rendering is expensive, parent re-renders for
unrelated reasons. Useless when: props always differ, component is cheap, or it
re-renders from its own state/context anyway.useMemo(fn, deps) — cache computed values. Only for genuinely expensive work or
preserving references passed to memoized children.useCallback(fn, deps) — cache function references. Use when passing callbacks to
memoized children, in custom hooks returning functions, or as Effect dependencies.async functions.useState, useEffect, or any client-side React APIs."use client" at file top."use server" marks Server Functions (Actions callable from client), not Server
Components.Start rendering immediately, stream slower parts as they resolve. Create promises in
Server Components, pass to Client Components, read with use() inside <Suspense>.
import { Button } from '@/components/Button' not
from '@/components'.lazy(() => import('./Chart')) with <Suspense> for heavy components.Promise.all, never sequential awaits.Use onCaughtError and onUncaughtError root options on createRoot for fine-grained
error reporting — caught errors come from Error Boundaries, uncaught from unhandled throws.
Tests resemble how users interact with the application. Query by what users see (roles, text, labels), not by implementation details (class names, component internals, test IDs).
Always use screen for queries — never destructure from render(). Set up
userEvent.setup() before rendering.
Use the highest-priority query that works: getByRole > getByLabelText >
getByText > getByTestId (last resort). Use getBy for present elements, queryBy
for asserting absence, findBy for async appearance. Full query priority and variant
tables in references/testing.md.
Always prefer userEvent over fireEvent — it simulates real user behavior (focus,
blur, keyDown/keyPress/keyUp sequence). Full method catalog in references/testing.md.
waitFor for assertions that need to wait for async operations.waitFor callback — multiple assertions cause slower failure detection.waitFor — the callback may run multiple times.waitFor.findBy over waitFor + getBy.Render the component and interact as a user would. For useFormStatus components,
ensure the component is rendered inside a <form> with an action prop.
act unnecessarily — render() and fireEvent already handle it.
If you see act warnings, fix the root cause (state update after test finishes).role attributes to native elements — <button> already has role="button".type and <label> — this makes them queryable by role.cleanup manually — it's automatic.@testing-library/jest-dom matchers: toBeInTheDocument(), toBeVisible(),
toBeDisabled(), toHaveTextContent(), toHaveAttribute(), toHaveValue().When writing React code:
When reviewing React code:
This skill provides React-specific conventions. The coding skill governs workflow; language skills govern JS/TS choices; this skill governs component architecture, hooks, state management, and rendering discipline.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.