Last updated: Apr 29, 2026
React 19
React 19 shipped in December 2024. The release focuses on simplifying data mutations, moving work to the server, and removing boilerplate that developers had to manage manually in earlier versions.
Actions
An action is an async function used inside a transition. React automatically tracks its pending state, handles errors, and supports optimistic updates. useActionState (renamed from the earlier useFormState) is the primary hook for wiring an action to a form.
import { useActionState } from "react";
async function addItem(_prev: string[], formData: FormData) {
const name = formData.get("name") as string;
await saveToServer(name);
return [..._prev, name];
}
function ItemForm() {
const [items, submitAction, isPending] = useActionState(addItem, []);
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Add</button>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</form>
);
}
New Hooks
| Hook | Purpose |
|---|---|
useActionState | Track the return value and pending state of a form action |
useFormStatus | Read the submission status of a parent <form> from a child component |
useOptimistic | Show an optimistic value instantly while an async action settles |
use | Read a Promise or Context during render; unlike other hooks, can be called conditionally |
Server Components
Components can now run entirely on the server. Heavy dependencies stay out of the client bundle, and the rendered output streams to the browser as HTML. Any component that needs interactivity must be marked with 'use client' at the top of its file — everything else is a server component by default (in frameworks that support RSC).
ref as a Prop
Function components can accept ref as a regular prop. forwardRef is no longer needed.
function Input({ ref, ...props }: { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}
Document Metadata
<title>, <meta>, and <link> tags rendered inside any component are automatically hoisted to the document <head>. This removes the need for third-party helmet libraries in many cases.
function BlogPost({ title }: { title: string }) {
return (
<article>
<title>{title}</title>
<meta name="author" content="author" />
<p>Post content…</p>
</article>
);
}
React Compiler
The React Compiler is a separate build-time tool open-sourced alongside the release. It analyzes components and automatically inserts memoization, eliminating the need for manual useMemo, useCallback, and React.memo in most cases. It ships as an opt-in Babel plugin (babel-plugin-react-compiler) with a companion ESLint plugin (eslint-plugin-react-compiler) that surfaces Rules of React violations in your editor. The compiler depends on React 19 runtime APIs and needs to be installed separately.
Other Changes
- Improved error reporting — hydration mismatches now show diffs, and unhandled errors in boundaries include better stack traces.
- Stylesheet and async script support —
<link rel="stylesheet">and<script async>rendered in components are deduplicated and ordered correctly. - Resource preloading — new
preload,preinit, and related APIs let components hint which resources will be needed soon. - Custom elements — full support for Web Components, including proper attribute and property handling.
19.2 Updates
React 19.2 shipped in October 2025 — the third release in under a year, following 19.0 in December 2024 and 19.1 in June 2025.
Activity
<Activity> is a component that hides parts of the UI without destroying their state. It accepts a mode prop with two values:
"visible"— shows children, mounts effects, processes updates normally."hidden"— hides children via CSS, unmounts effects, defers updates until React has nothing else to do.
Unlike conditional rendering, switching to "hidden" preserves component state so it can be restored instantly.
import { Activity, useState } from "react";
function Tabs() {
const [tab, setTab] = useState<"home" | "settings">("home");
return (
<>
<nav>
<button onClick={() => setTab("home")}>Home</button>
<button onClick={() => setTab("settings")}>Settings</button>
</nav>
<Activity mode={tab === "home" ? "visible" : "hidden"}>
<HomePage />
</Activity>
<Activity mode={tab === "settings" ? "visible" : "hidden"}>
<SettingsPage />
</Activity>
</>
);
}
Both pages stay mounted, but only the active one runs effects and receives urgent updates. Common use cases include tab UIs, pre-rendering routes a user is likely to visit next, and preserving scroll position or form input on back navigation.
useEffectEvent
useEffectEvent extracts “event” callbacks out of effects so they always see the latest props and state without being listed as dependencies.
import { useEffect, useEffectEvent } from "react";
function ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {
const onConnected = useEffectEvent(() => {
showNotification("Connected!", theme);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on("connected", () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]);
}
Without useEffectEvent, theme would have to be in the dependency array, causing the effect to re-run and reconnect every time the theme changes. The hook keeps non-reactive logic out of the dependency list without suppressing the linter. It requires eslint-plugin-react-hooks@latest so the linter knows not to flag the omitted dependency.
useEffectEvent is meant for functions that are conceptually “events” fired from an effect — not as a general escape hatch to silence lint warnings.
cacheSignal
For React Server Components only. cacheSignal returns an AbortSignal tied to the lifetime of a cache() entry. When the cached result is no longer needed (render completed, aborted, or failed), the signal fires, allowing in-flight work to be cancelled.
import { cache, cacheSignal } from "react";
const fetchData = cache(async (url: string) => {
const res = await fetch(url, { signal: cacheSignal() });
return res.json();
});
Performance Tracks
Custom tracks now appear in the Chrome DevTools Performance panel:
- Scheduler track — shows what React is working on at each priority level (blocking for user interactions, transition for
startTransitionupdates). It surfaces when an update is blocked waiting on a different priority, or when React yields to the browser before continuing. - Components track — shows the tree of components being rendered or running effects, with labels like “Mount” and “Blocked”. Helps pinpoint which components are slow to render or have expensive effects.
Partial Pre-rendering
A new capability that splits SSR into two phases: pre-render a static shell ahead of time, then resume rendering on the server to fill in dynamic content later.
import { prerender } from "react-dom/static";
import { resume } from "react-dom/server";
const controller = new AbortController();
const { prelude, postponed } = await prerender(<App />, {
signal: controller.signal,
});
await savePostponedState(postponed);
const postponedState = await getPostponedState(request);
const stream = await resume(<App />, postponedState);
The static prelude can be served from a CDN. When a request comes in, resume picks up where pre-rendering left off and streams the remaining HTML. A resumeAndPrerender variant produces complete static HTML for full SSG.
Other 19.2 Changes
- Batched Suspense reveals on SSR — server-streamed Suspense boundaries are now batched for a short window before revealing, matching client-side behavior and allowing animations to run on larger batches of content.
- Web Streams for Node SSR —
renderToReadableStream,prerender,resume, andresumeAndPrerendernow work in Node.js (though Node Streams remain recommended for performance). - eslint-plugin-react-hooks v6 — ships flat config by default in the
recommendedpreset, with opt-in compiler-powered rules. Legacy config moves torecommended-legacy. - useId prefix change — default prefix changed from
:r:/«r»to_r_for compatibility with View Transitions and XML 1.0 names.