Skip to content

React 19 prompt

Rules that make your coding agent write React 19.3 as it is: ref as a prop, Actions and form state, use() with Suspense, effects only where they belong, and the React Compiler.

What it makes your agent good at

Agents trained on React 16 to 18 still wrap components in forwardRef, render Context.Provider, fetch and derive state in effects, and hand-write useMemo where the React Compiler already memoizes. This prompt gives them the current model: refs and context as plain props, Actions with useActionState, useOptimistic and useFormStatus, use() for promises, useEffectEvent and Activity, and none of the APIs React 19 removed.

  1. No forwardRefref as a regular prop, ref callbacks that return a cleanup, and context rendered as its own provider.
  2. Actions for formsuseActionState for returned errors, useFormStatus in the submit button, and useOptimistic inside the Action.
  3. use() with real promisesPromises from Server Components or a cache, read with use() under Suspense, never created during render.
  4. Fewer effectsDerived values in render, keys to reset state, handlers for events, and useEffectEvent for the latest props.
  5. Compiler-aware memoizationNo new useMemo or useCallback when the React Compiler is on, pure renders, and use no memo only as an escape hatch.
  6. Only what React 19 still hasNo ReactDOM.render, string refs, legacy context, or propTypes and defaultProps on function components.

The React 19 rules

127 lines, current to React 19.3 and TypeScript. Last reviewed October 23, 2026. Plain Markdown, no front matter: each agent's is below.

Download

react-19.md

# React 19

You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.

## Before you write code

- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.
- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.
- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.

## Components, refs and context

- Write function components. The only class you may need is an error boundary, and only when the framework (Next.js `error.tsx` or `catchError`) or a library in the project doesn't give you one.
- `ref` is a regular prop. Never wrap a component in `forwardRef`:

```tsx
export function Input({ ref, ...props }: React.ComponentProps<"input">) {
  return <input ref={ref} data-slot="input" {...props} />;
}
```

- A ref callback may return a cleanup function, which React calls on detach instead of calling the ref with `null`. Write ref callbacks with a block body: an arrow that returns an assignment is now a type error.
- `useRef` takes an initial value: `useRef<HTMLDivElement>(null)`. Never read or write `ref.current` during render.
- Render a context as its own provider: `<ThemeContext value={theme}>`. Read it with `use(ThemeContext)`, which, unlike `useContext`, works inside conditions.
- Defaults come from destructuring (`{ size = "md" }`), types from TypeScript: no `defaultProps` or `propTypes` on function components. Use the `React.JSX` namespace, not a global `JSX`.
- Key list items by a stable id, never the index of a list that changes. Change a component's `key` to reset its state.

## Data: use, Suspense and Server Components

- `use(promise)` reads a promise during render: it suspends until the promise resolves and sends a rejection to the nearest error boundary. The promise must come from outside render (a Server Component prop, a cache, a data library). Never `use(fetch(…))` or a promise created in the component: each render makes a new one and suspends again. Don't wrap `use` in `try`/`catch`.
- Give each `<Suspense>` a fallback with the content's layout, placed where content should reveal together.
- Server Components (where supported) are async functions that await data directly, run only on the server and ship no JavaScript: no state, effects or browser APIs. Wrap shared reads in `cache()` so one request reads once. `cacheSignal()` returns a signal that aborts when that render's cache scope ends; pass it to long work.
- `"use client"` marks the module where client code starts, and everything it imports is client code: put it on interactive leaves, not pages. Props crossing it must be serializable: no functions except Server Functions, no class instances.
- `"use server"` marks Server Functions, at the top of a file or of an async function. They are public endpoints: validate input, check the session and authorize inside each one. Modules holding secrets start with `import "server-only"`.

## Actions and forms

An Action is an async function run in a transition: passed to `<form action>` or `<button formAction>`, or called inside `startTransition`. React tracks its pending state, sends thrown errors to the error boundary, and resets the form's uncontrolled fields after a successful form action.

```tsx
"use client";

import { useActionState } from "react";
import { useFormStatus } from "react-dom";

type State = { error?: string; name?: string };

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button type="submit" disabled={pending}>{pending ? "Saving…" : "Save"}</button>;
}

export function NameForm({ save }: { save: (prev: State, data: FormData) => Promise<State> }) {
  const [state, formAction] = useActionState(save, {});
  return (
    <form action={formAction}>
      <input name="name" defaultValue={state.name} aria-invalid={Boolean(state.error)} />
      {state.error && <p role="alert">{state.error}</p>}
      <SubmitButton />
    </form>
  );
}
```

- `useActionState(action, initialState)` returns `[state, dispatch, isPending]`; the action receives `(previousState, payload)`. Return expected errors (and the submitted values, so fields survive the reset) as state; throw only for bugs. Outside a form, call `dispatch` inside `startTransition`.
- `useFormStatus()` comes from `react-dom` and reads the parent `<form>`: call it in a component rendered inside the form, never in the component that renders it.
- `useOptimistic(value, update)` shows the expected result while an Action runs. Call its setter inside the Action; when the Action settles it shows `value` again, so the real result must arrive through props or state.
- State updates after an `await` inside a transition are not part of it: wrap them in `startTransition` again.
- Prefer uncontrolled inputs with `name` and `defaultValue` for forms read as `FormData`; control an input only when its value drives the UI while typing. Reset a form manually with `requestFormReset(form)` from `react-dom`.

## Effects

Effects synchronize with systems outside React: subscriptions, timers, imperative widgets, browser APIs. Before writing one, check that it isn't one of these:

- A value computed from props or state: compute it during render.
- State to reset when a prop changes: render the component with `key={id}`.
- A reaction to a user event: put it in the event handler.
- Data fetching, when the framework has Server Components or loaders, or the project has a data library. When you must fetch in an effect, abort or ignore stale responses in the cleanup.
- Telling a parent about a change: call the parent's callback in the handler that caused it.

Every subscription returns a cleanup; Strict Mode runs an extra setup and cleanup in development to prove it works. List every reactive value as a dependency and never silence the lint rule.

`useEffectEvent` reads the latest props and state from inside an effect without making them dependencies:

```tsx
const onConnected = useEffectEvent(() => showToast(`Connected to ${roomId}`, theme));
useEffect(() => {
  const connection = connect(roomId);
  connection.on("connected", () => onConnected());
  return () => connection.close();
}, [roomId]);
```

Call effect events only from effects; never list them as dependencies or pass them to other components. Subscribe to external stores with `useSyncExternalStore`, not an effect that sets state. `useLayoutEffect` is only for measuring before paint.

## Activity, transitions and View Transitions

- `<Activity mode={visible ? "visible" : "hidden"}>` hides its children while keeping their state and DOM, cleans up their effects while hidden and renders them at low priority. Use it for tabs, panels and back navigation where state should survive, instead of unmounting.
- `startTransition` or `useTransition` for non-urgent updates (switching tabs, filtering a long list); `useDeferredValue(value, initialValue)` keeps typing responsive while a slow child catches up.
- `<ViewTransition>` animates updates made in transitions, deferred values and Suspense reveals, never a plain `setState`; tag them with `addTransitionType`. Give named elements `default="none"` unless they should animate on every transition.

## Metadata and resources

- `<title>`, `<meta>` and `<link>` rendered in any component are hoisted into `<head>`. When the framework has a metadata API (Next.js `metadata`), use that instead.
- A stylesheet rendered as `<link rel="stylesheet" href="…" precedence="default" />` is deduplicated and ordered, and Suspense waits for it to load. `<script async src="…">` is deduplicated too.
- Resource hints come from `react-dom`: `preload(href, { as: "font", type, crossOrigin: "" })`, `preinit(href, { as: "script" | "style" })`, `preconnect` and `prefetchDNS`, called during render or in handlers.

## React Compiler

- When it is on, don't add `useMemo`, `useCallback` or `memo` for performance in new code. Leave existing ones unless you test their removal: it can change effect dependencies.
- It relies on the Rules of React: render is pure (no side effects, no mutating props, state or values after they're used in JSX), hooks run at the top level, and refs are not read during render. Code that breaks them is skipped or behaves wrongly.
- `"use no memo"` at the top of a component or hook opts it out: a temporary escape hatch with a comment saying why. Under `compilationMode: "annotation"`, `"use memo"` opts in.
- Lint with `eslint-plugin-react-hooks`' recommended config, which includes the compiler's rules.

## Do not

- Do not use APIs React 19 removed: `ReactDOM.render`, `hydrate`, `unmountComponentAtNode` and `findDOMNode` (use `createRoot` or `hydrateRoot` from `react-dom/client`, and refs), string refs, legacy context (`contextTypes`, `getChildContext`), `act` from `react-dom/test-utils` (it is in `react`), or `react-test-renderer/shallow`.
- Do not write `forwardRef`, `<Context.Provider>`, `useFormState` (now `useActionState` from `react`) or `element.ref` in new code.
- Do not mirror props into state, chain effects that set state, or fetch in `useEffect` where the framework or a data library can.
- Do not read `Date.now()` or `Math.random()`, or format with the runtime's locale, during render in server-rendered components: they cause hydration mismatches.

## Before you finish

- Run the type checker and the linter with the React hooks rules on, and fix what they report.
- Run in development with Strict Mode: no warnings, no double requests, no leaked subscriptions.
- Submit every form you touched: pending state, returned errors, reset fields, and, with Server Functions, a submit before hydration.
- Check the console for hydration mismatch warnings on server-rendered pages.

10,317 characters, 1,531 words. Under Windsurf's 12,000-character limit for a rule, as every prompt here is.

Use it in your agent

The same rules, where your agent reads them. Each file has the front matter its agent needs, set to apply on every request.

Claude Code

A rule it loads every session

  1. Save it as .claude/rules/react-19.md in your project, front matter included, or run the install command.
  2. Claude Code loads every file in .claude/rules with your CLAUDE.md when a session starts.
  3. Prefer one file? Paste it into CLAUDE.md under its own heading instead.

.claude/rules/react-19.md

# React 19You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.## Before you write code- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.## Components, refs and context…

Or let the shadcn CLI write this file: sign in (free) for your command.

Cursor

A project rule with front matter

  1. Save it as .cursor/rules/react-19.mdc, front matter included, or run the install command.
  2. alwaysApply: true adds it to every Agent request in this project.
  3. Check it under Cursor Settings, Rules, then commit the folder for your team.

.cursor/rules/react-19.mdc

---description: "React 19 component rules: refs and context, use and Suspense, Actions and forms, effects, Activity, metadata and the React Compiler."alwaysApply: true---# React 19You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.## Before you write code- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.## Components, refs and context…

Or let the shadcn CLI write this file: sign in (free) for your command.

Windsurf

A workspace rule for Cascade

  1. Save it as .windsurf/rules/react-19.md, or run the install command.
  2. trigger: always_on applies it to every Cascade request in this workspace.
  3. Windsurf reads up to 12,000 characters per rule; this one is 10,317.

.windsurf/rules/react-19.md

---trigger: always_on---# React 19You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.## Before you write code- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.## Components, refs and context…

Or let the shadcn CLI write this file: sign in (free) for your command.

GitHub Copilot

Instructions for the repository

  1. Save it as .github/instructions/react-19.instructions.md, or run the install command.
  2. applyTo: "**" applies it to every request in this repository.
  3. Or paste it into .github/copilot-instructions.md, which Copilot reads for every request.

.github/instructions/react-19.instructions.md

---description: "React 19 component rules: refs and context, use and Suspense, Actions and forms, effects, Activity, metadata and the React Compiler."applyTo: "**"---# React 19You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.## Before you write code- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.## Components, refs and context…

Or let the shadcn CLI write this file: sign in (free) for your command.

Codex & AGENTS.md

The file most agents read

  1. Paste it into AGENTS.md at your project's root, under its own heading.
  2. Codex reads AGENTS.md before it starts work, and so do Cursor, Zed, Jules, Amp and Copilot's coding agent.
  3. A folder can have its own AGENTS.md: the one closest to the file being edited wins.

AGENTS.md

## React 19You are working in a React 19.3 project with TypeScript. Write code for the version in `package.json`: React 19 removed legacy APIs, made refs and context simpler, and added Actions, `use`, `useEffectEvent`, `<Activity>` and `<ViewTransition>`. Much of what you remember from React 16 to 18 is now the long way round, or gone.### Before you write code- Check `react`, `react-dom` and `@types/react` (all 19.x) and the framework (Next.js, React Router, TanStack Start or a Vite app). Server Components and Server Functions exist only where the framework supports them; follow its docs for routing and data.- Check whether the React Compiler is on: `babel-plugin-react-compiler` in devDependencies, `reactCompiler` in `next.config.*`, or the compiler in the Vite React plugin's options. It changes the memoization rules below.- When unsure an API exists, read `node_modules/@types/react/index.d.ts` and `node_modules/@types/react-dom/index.d.ts`, not memory.### Components, refs and context…

AGENTS.md is shared with your own instructions, so paste the rules in: the copy sets their headings a level down.

Rules teach it. The MCP equips it. With Pro, your agent installs every component, block and icon it needs, from a prompt.

Get Pro

Pricing

Give your agent the parts, with Pro

The prompts are free. Every Pro plan adds the MCP server, so your agent installs the components, blocks and icons it writes code around.

4.9/5 out of 5 · 150+ reviewsfrom 150+ founders and developers.

Billed once a year in USD. Cancel anytime.

Individual

For personal projects & open source

$32instead of $49/mo

$389 billed yearly

  • How many people can use the license.Seats: 1 seat
  • Client work, SaaS products and anything you charge for.Commercial use: Personal & open source only
  • Page sections in 56 categories, from heroes and pricing tables to dashboards and settings, on every framework and UI library, in your theme.Blocks: All 6,181 blocks
  • Complete Next.js sites for startups, SaaS, agencies and portfolios, every page designed, ready to ship.Templates: All 14 templates
  • Your AI agent searches, previews and installs any component, block, icon or DESIGN.md from a prompt: 22 tools in Claude Code, Cursor, VS Code and 35 more clients.MCP server: 38 AI clients
  • Buttons with motion and states of their own, from an arrow that slides in to a burst of confetti.Animated buttons: All 35 buttons
  • Typewriters, scrambles, counters and more, for headlines, labels and numbers.Text effects: All 97 effects
  • Animated backgrounds for heroes, sections and cards, from a soft aurora to a flock that flees the cursor. Most are pure CSS.Backgrounds: All 100 backgrounds
  • WebGPU shaders, from a slow aurora to a black hole, each drawing only while it's on screen.Shaders: All 100 shaders
  • One design and one theme on all four: the components, blocks and effects are built for each framework.Frameworks: React, Vue, Svelte, Solid
  • React on Base UI, Radix UI, React Aria or Ark UI: install the version for the library your project already uses. Vue, Svelte and Solid run on Ark UI.UI libraries: Base UI, Radix, Aria, Ark
  • From a button to a date picker, a color picker or a guided tour: accessible, typed in TypeScript, each with live demos.Components: All 174 components
  • Real-world patterns built from the components, in 53 galleries: forms, tables, menus, dialogs and more.Examples: All 1,103 examples
  • Area, bar, line, pie, radar and radial charts on TanStack Charts, on every framework. React also gets the Recharts originals.Charts: All 70 charts
  • Debounce, local storage, media queries and more: each hook is one typed file you own.React hooks: All 35 hooks
  • From 222 libraries, Lucide, Heroicons, Tabler and Material Symbols among them, as SVG or a React component in one command.Icons: 294k+ icons
  • Stripe, Linear, Apple and 497 more: a brand's colors, typography and components in one file your AI agent builds from.DESIGN.md files: All 500 brands
  • Restyle every component and block at once: 8 styles, 7 grays, 18 colors and 5 radii, shared as a shadcn preset code.Theme builder: 8 styles × 18 colors
  • Priority requests are answered first.Support: Standard

Team

Most popular

For freelancers, agencies & startups

$66instead of $99/mo

$789 billed yearly · ≈ $13/dev/mo

Everything in Individual, plus

  • How many people can use the license.Seats: Up to 5 seats
  • Client work, SaaS products and anything you charge for.Commercial use: Included
  • Priority requests are answered first.Support: Priority

Organization

For companies with larger teams

$132instead of $199/mo

$1,589 billed yearly

Everything in Team, plus

  • How many people can use the license.Seats: Unlimited seats
  • Secure checkout via Stripe
  • Code lives in your repo
  • Keep what you've shipped if you cancel
  • License terms

React 19 prompt FAQ

What the React 19 prompt covers and how to use it in your agent.

How does the React 19 prompt change memoization when the React Compiler is on?

The agent first checks whether the compiler is enabled. If it is, new code gets no useMemo, useCallback or memo for performance, and existing ones stay unless their removal is tested, since removing them can change effect dependencies. Renders stay pure so the compiler can optimize them.

When does the React 19 prompt allow the use no memo directive?

Only as a temporary escape hatch for a component or hook that misbehaves under the React Compiler, with a comment saying why. The real fix is to follow the Rules of React: pure renders, hooks at the top level and no ref reads during render.

4.9 out of 5 from 150+ developers

Rules teach it. The MCP equips it.

Every component, block and icon, installed by your AI agent on your stack. The prompts stay free.

Get Pro
  • Cancel anytime
  • Keep what you've shipped