From frontend
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.
npx claudepluginhub xobotyi/cc-foundry --plugin frontendThis skill uses the workspace's default tool permissions.
**Components are pure functions. State is minimal. Effects are escape hatches. If you reach for useEffect, verify you
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Components 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.
${CLAUDE_SKILL_DIR}/references/components.md Composition, refs, metadata, custom elements${CLAUDE_SKILL_DIR}/references/hooks.md Hook rules, custom hooks, useSyncExternalStore${CLAUDE_SKILL_DIR}/references/state.md Placement, reducers, context, actions${CLAUDE_SKILL_DIR}/references/performance.md Compiler, memoization, server components, streaming${CLAUDE_SKILL_DIR}/references/testing.md Query priority/variants, userEvent catalog, async patternsBuild 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.
Local mutation is fine. Creating and mutating objects/arrays within the same render is safe — the mutation is invisible outside that render.
Event handlers don't need to be pure — they run outside of rendering.
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:
key={userId} on the componentitems.find(...) during renderonChange in the event handlerYou 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
useState, useReducer, ContextNever 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.
useStateuseReduceruseReduceruseReducerReducer 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 ${CLAUDE_SKILL_DIR}/references/testing.md.
Always prefer userEvent over fireEvent — it simulates real user behavior (focus, blur, keyDown/keyPress/keyUp
sequence). Full method catalog in ${CLAUDE_SKILL_DIR}/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.