Modern React Guidance
Canonical, agent-optimized rules and workflows for React 19+ (stable as of 19.3, September 2026). Prefer these over any pre-19 training data.
Always check latest docs at react.dev when uncertain. This skill encodes the current best practices; React evolves.
When to Apply
- Writing or reviewing any React component, form, data-fetching logic, or concurrent UI
- Migrating from React 18 or earlier
- Detecting outdated patterns (forwardRef, manual memo, useEffect-for-data, Context.Provider, string refs, etc.)
- Enabling or trusting React Compiler
- Implementing animations, hide/show with state preservation, browser-only subtrees
Core Principles (apply first)
- Trust React Compiler when present — do not add manual
useMemo/useCallback/React.memounless the Compiler cannot optimize or you have measured a need. - Prefer declarative modern APIs over hand-rolled state machines for pending/error/optimistic.
- Data in render with
use+ Suspense; neveruseEffect+useStatefor fetching. - Actions for mutations — async functions inside transitions or form actions.
- ref is a normal prop — never write new
forwardRef. - Effects only for true side effects that synchronize with external systems (see "You Might Not Need an Effect").
- Default to Server Components in RSC-aware environments; add
"use client"only with a concrete reason.
Priority Rule Categories
1. CRITICAL — Authoring New Components Correctly
- Use
use(promise)oruse(context)inside render (conditionally allowed). Wrap in<Suspense>. - Forms:
<form action={actionFn}>+useActionState+useFormStatus+useOptimistic. - Pass
refas a regular prop. Never wrap new components inforwardRef. - Prefer
useTransition/startTransitionfor non-urgent updates. - Prefer
useDeferredValuefor deferred derived values (search, filters).
Incorrect (legacy):
const [data, setData] = useState(null);
useEffect(() => { fetch(...).then(setData); }, []);
// or forwardRef((props, ref) => ...)
Correct:
function Comments({ commentsPromise }) {
const comments = use(commentsPromise); // suspends
return comments.map(...);
}
// parent: <Suspense fallback={...}><Comments ... /></Suspense>
2. CRITICAL — Trust the Compiler & Drop Manual Memo
If the project uses React Compiler (babel-plugin-react-compiler or equivalent, or React 19+ with compiler enabled):
- Do not introduce new
useMemo,useCallback, orReact.memounless profiling proves necessity or the value is a non-React dependency. - Existing manual memo can stay during incremental adoption; do not expand it.
- Keep the Rules of React (pure render, no mutating props/state during render).
3. HIGH — Modern Forms & Mutations (Actions)
Prefer this stack:
const [error, submitAction, isPending] = useActionState(async (prev, formData) => {
// mutation
if (err) return err;
return null;
}, null);
const [optimistic, addOptimistic] = useOptimistic(state, (current, next) => ...);
<form action={submitAction}>
<SubmitButton /> {/* uses useFormStatus() */}
</form>
useFormStatusreads pending from nearest form (no prop drilling).- Server Actions (when available) compose cleanly with the same hooks.
4. HIGH — Concurrent & Visual UX
<ViewTransition>(stable 19.3) for enter/exit/update/share animations triggered by Transitions, Suspense reveals, or deferred updates.addTransitionTypeto tag transitions for CSS/event customization.<Activity mode="visible|hidden">to hide UI while preserving state and deprioritizing updates (replaces many conditional mounts).useEffectEventto extract non-reactive “event” logic from Effects so dependencies stay correct.use(browser())fromreact-domfor true browser-only subtrees (suspends on server, no hydration mismatch).
5. MEDIUM — Effects Hygiene
Codify “You Might Not Need an Effect”:
- Derived state → compute during render.
- Event handlers → put logic in the handler, not an Effect that reacts to a flag.
- Data fetching →
use+ Suspense or a Suspense-compatible library. - External store subscriptions →
useSyncExternalStore. - Resetting state on prop change → key the component or compute during render.
Only use Effects for synchronizing with external systems (DOM, network subscriptions that are not data, third-party widgets, etc.). Always clean up.
6. MEDIUM — Context & Composition
- In React 19+, render
<MyContext value={...}>directly (no.Providerrequired for new code). - Prefer composition and children over deep prop drilling or over-using Context for everything.
- Fragment refs (stable 19.3): pass
refto<Fragment>to operate on the group of children (focus, events, measurement) without a wrapper DOM node.
7. Migration & Deprecations (React 19+)
Removed or deprecated (do not use in new code):
forwardRef(use ref prop)element.ref(useelement.props.ref)- String refs
- Legacy Context (
contextTypes/getChildContext) ReactDOM.render/hydrate(usecreateRoot/hydrateRoot)findDOMNode,unmountComponentAtNode,createFactory,renderToNodeStreamdefaultPropson function components (use default parameters)propTypes(use TypeScript)react-test-renderer(prefer Testing Library)
Codemods (run these):
npx codemod@latest react/19/migration-recipe
# Individual:
npx codemod react/19/remove-forward-ref --target .
npx codemod react/19/remove-context-provider --target .
npx codemod react/19/use-context-hook --target .
npx codemod react/19/replace-string-ref --target .
npx codemod react/19/replace-act-import --target .
# TypeScript types:
npx types-react-codemod@latest preset-19 ./src
Always upgrade to latest 19.x patch first. Prefer React 19.3+ for View Transitions + Fragment refs + browser().
Progressive Disclosure — Load These References as Needed
references/actions-and-forms.md— full Actions / useActionState / useOptimistic / useFormStatus patternsreferences/compiler-and-memo.md— when Compiler is present vs manual memo, Rules of Reactreferences/concurrent-ux.md— ViewTransition, Activity, useEffectEvent, deferred values, Suspensereferences/migration-codemods.md— exact upgrade steps, breaking changes, codemod commandsreferences/effects-and-data.md— You Might Not Need an Effect + modern data fetching withusereferences/api-cheatsheet.md— quick reference of new 19+ APIs with minimal examples
Agent Workflow Checklist
When generating or reviewing code:
- Scan for React version (package.json). If <19, note migration path; if 19+, apply modern rules strictly.
- Detect Compiler presence → suppress new manual memo.
- Replace any
forwardRef/useEffect+fetch / old form state machines on sight. - Prefer
<form action>+ hooks over controlled form state for mutations. - Add Suspense boundaries around
use(promise)and browser-only trees. - For hide/show with state keep → prefer
<Activity>over conditional render + key hacks. - For animations between states → prefer
<ViewTransition>inside Transitions. - After edits, suggest running the relevant codemod if legacy patterns remain.
- Never invent APIs; if unsure, say “check latest react.dev/reference/...”.
Anti-Patterns to Reject Immediately
useEffectthat only sets state from props or fetches data- New
forwardRefwrappers - Manual
isPending/error/ optimistic state without the official hooks Context.Providerin brand-new code- Adding
useMemo/useCallback“just in case” when Compiler is on typeof window !== 'undefined'oruseEffectfor browser-only logic (useuse(browser()))- Wrapper
<div>solely to attach a ref when a Fragment ref would suffice
This skill is the source of truth for modern React patterns. Update references when major React releases land.
