programming-react
SkillReact architecture: hooks, state boundaries, server components, and library choices that scale. Auto-activates in React projects.
Instructions
Overview
Build React interfaces with explicit state ownership, pure rendering, and dependable loading and mutation behavior. Research baseline: 2026-09-05, React 19.2 stable and React Compiler 1.0 stable. Inspect installed React, framework, renderer, and compiler configuration first; documentation can include Canary-only APIs. Keep existing routing, styling, and state libraries unless the task requires changing them.
Mental model
A render describes UI from a snapshot of props, state, and context; React may repeat or abandon it. Events cause user-directed changes, while effects synchronize committed UI with external systems. React's server component model requires supporting infrastructure; it is not automatically enabled by using React 19.
State and component boundaries
- Store the smallest authoritative state. Derive filtered lists, counts, and validation summaries during render; do not mirror props or derived values through effects.
- Keep state near its consumers. Lift it to the closest shared owner when necessary; use composition before introducing context or a global store. Shareable navigation state belongs in the URL.
- Use functional updates when the next value depends on pending previous state. Treat props/state as immutable snapshots; mutate neither during rendering.
- Model mutually exclusive UI states explicitly. Keep initial loading, refreshing existing data, empty results, and failures distinguishable; a failed fetch is not an empty list.
- Define components at module scope to preserve identity. Use stable domain keys for reorderable lists; change a subtree's key only when resetting its state is intended.
- Make props reflect behavior rather than internal switches. Follow framework export conventions; neither named exports nor one-component-per-file is universally required.
Effects and current APIs
- Hooks belong at the top level of components/custom hooks, before conditional returns. React's
use(resource)is an exception: it permits conditions and loops, but still belongs inside a component or hook and cannot be wrapped intry/catch. - Effects must list their reactive dependencies and clean up subscriptions, timers, and pending work. Strict Mode's extra development setup/cleanup cycle exposes lifecycle errors; do not hide it with a "run once" ref.
- React 19.2's
useEffectEventreads current values for non-reactive logic called from effects. It is not a dependency-avoidance trick, a UI event handler, or a callback to pass elsewhere. - React 19 permits function components to receive
refas a prop. Use this for new 19-targeted APIs; preserveforwardRefwhere older consumers need it. - React 19.2's
Activitycan hide UI while preserving state and cleaning up its effects. Use it when that lifecycle is needed; ordinary conditional rendering remains appropriate. - React Compiler can memoize supported code when enabled in the build. Do not assume it is active because React is installed. Profile before adding manual memoization; memoization must never be required for correctness.
Data, server boundaries, and forms
- Prefer existing framework loaders or cache infrastructure for shared server data. A small effect-based fetch is valid when it handles races, cancellation, and errors; a cache library is not mandatory.
- Suspense handles participating data sources, lazy components, and promises consumed through
use; it does not detect arbitrary effect-based fetching. Reuse a stable promise rather than creating one every client render. - Use Server Components only in a supporting framework. Keep secrets server-side, and authorize each server mutation.
"use client"marks a module dependency boundary; Client Components can still be prerendered on the server. - Use native form semantics and labels. Controlled inputs are useful when React must drive their value; uncontrolled inputs and
FormDatasuit many ordinary forms. - React 19 form actions and
useActionStateprovide pending state and action results.useFormStatusobserves its parent form, not a form rendered by the same component. Follow existing form infrastructure when it already meets the need. - Show expected validation failures as action state. Let unexpected failures reach the appropriate error boundary; handle event-handler and unrelated asynchronous failures explicitly because boundaries do not catch every error.
- Keep optimistic state temporary and reconciled with authoritative results. Failed or conflicting mutations need recovery; avoid optimistic updates when the operation cannot be safely predicted.
Example
Derive visible content directly and retain stable item identity:
type Item = { id: string; title: string; archived: boolean };
function ItemList({ items, showArchived }: {
items: readonly Item[];
showArchived: boolean;
}) {
const visible = items.filter(item => showArchived || !item.archived);
return (
<ul>
{visible.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
);
}No effect or duplicate state is needed. The caller remains responsible for distinguishing loading, failure, and a successful empty result.
Checklist
- Confirm the installed stable APIs and the framework's server/client model.
- Give each value one owner; derive values without synchronization effects.
- Check hook ordering, effect dependencies, cleanup, and stable identity.
- Exercise keyboard interaction, labels, pending/error states, and mutation reconciliation.
- Use existing installed lint, type-check, component, and critical-flow tests when authorized. Report what was source-reviewed versus executed.