The biggest React release in years — Actions, new hooks, and a smarter runtime
React 19 (stable since December 5, 2024) ships a complete rethinking of how mutations and async work is handled. The release introduces Actions, four brand-new hooks, the use API, fully-stable Server Components, and a raft of ergonomic improvements that reduce boilerplate across the board.
The new primitive for async mutations — pending state, errors, and optimistic updates handled automatically.
Before React 19 you had to wire up isPending, error, and multiple useState calls every time you submitted data. React 19 introduces Actions: async functions passed to transitions (or to <form action={}>) that React manages for you.
❌ Before — manual plumbing
function UpdateName() { const [name, setName] = useState(""); const [error, setError] = useState(null); const [isPending, setIsPending] = useState(false); const handleSubmit = async () => { setIsPending(true); const error = await updateName(name); setIsPending(false); if (error) { setError(error); return; } redirect("/path"); }; return ( <div> <input value={name} onChange={e => setName(e.target.value)} /> <button onClick={handleSubmit} disabled={isPending} > Update </button> {error && <p>{error}</p>} </div> ); }
✅ After — useTransition with async Action
function UpdateName() { const [name, setName] = useState(""); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); const handleSubmit = () => { startTransition(async () => { const error = await updateName(name); if (error) { setError(error); return; } redirect("/path"); }); }; return ( <div> <input value={name} onChange={e => setName(e.target.value)} /> <button onClick={handleSubmit} disabled={isPending} > Update </button> {error && <p>{error}</p>} </div> ); }
Pending state
isPending is true from start to final commit — zero manual setters
Optimistic updates
Via useOptimistic — instant UI feedback while the server responds
Error handling
Errors propagate to Error Boundaries and revert optimistic state
Form reset
<form action={fn}> resets uncontrolled inputs after success
useActionState 🪝The one-stop hook that wraps your Action and hands back state, a callable Action, and a pending flag.
useActionState accepts an async Action function and an initial state. It returns [state, dispatchAction, isPending]. Every time you call dispatchAction, React invokes your function, tracks pending, and stores whatever you return as the new state.
useActionState pattern
import { useActionState } from "react"; async function updateNameAction(prevState, formData) { const name = formData.get("name"); const error = await updateName(name); if (error) return error; // stored as state redirect("/profile"); return null; } function RenameForm() { const [error, submitAction, isPending] = useActionState(updateNameAction, null); return ( <form action={submitAction}> <input type="text" name="name" /> <button type="submit" disabled={isPending}> {isPending ? "Saving…" : "Save"} </button> {error && <p className="text-red-500">{error}</p>} </form> ); }
useActionState was previously called ReactDOM.useFormState in Canary. It has been renamed and useFormState is now deprecated.useActionState — Live DemoThis form uses useActionState to manage async mutation state. React auto-tracks isPending, the previous state, and the latest result — no useState required.
isPending: false
state.status: idle
useFormStatus 📋Read the parent form's submission status from any child component — no prop drilling needed.
Design-system components (buttons, spinners, inputs) often need to know whether their surrounding form is submitting. With useFormStatus they can read that state independently, like a Context consumer — but without any setup.
useFormStatus in a design-system button
import { useFormStatus } from "react-dom"; // This component lives deep inside the form function SubmitButton() { const { pending, data, method, action } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? "Submitting…" : "Submit"} </button> ); } // Usage — SubmitButton reads form status automatically function CheckoutForm() { return ( <form action={checkoutAction}> <input name="card" /> <SubmitButton /> {/* no props needed */} </form> ); }
pendingbooleantrue while the form action is executing
dataFormData | nullthe form data being submitted
method"get" | "post"HTTP method of the form
useFormStatus — Deep-Nested FieldsuseFormStatus lets child components — even buried deep in a component tree — subscribe to their ancestor form's pending state without prop-drilling.
Key insight:
Neither FormInput nor SubmitButton receive any props about pending state — they call useFormStatus() themselves, reading the nearest parent form's state.
useOptimistic 🚀Instantly reflect a UI change while the server is still processing — then reconcile when done.
useOptimistic takes a piece of state and returns an optimistic copy of it. You call addOptimistic inside an Action to immediately show the expected result. React automatically rolls back to the real state once the async operation settles.
Optimistic todo list
import { useOptimistic } from "react"; function TodoList({ todos, addTodo }) { // optimisticTodos mirrors todos PLUS any pending add const [optimisticTodos, addOptimistic] = useOptimistic(todos); const formAction = async (formData) => { const text = formData.get("todo"); // ✅ Paint the new item immediately addOptimistic([...optimisticTodos, { text, pending: true }]); // Server round-trip (may take 1-2s) await addTodo(text); // React automatically switches to the real server state }; return ( <> <ul> {optimisticTodos.map((todo, i) => ( <li key={i} style={{ opacity: todo.pending ? 0.5 : 1 }}> {todo.text} </li> ))} </ul> <form action={formAction}> <input name="todo" /> <button type="submit">Add</button> </form> </> ); }
useOptimistic — Live DemoMessages appear instantly in a "pending" state while the server roundtrip completes. On failure, React auto-reverts to the last confirmed state.
Each message simulates a 1.5s network delay. Notice the instant optimistic paint, then the "confirmed" swap.
use API 🎣Read a Promise or a Context value at any point during render — even inside if blocks and loops.
use is a new React API (not a hook) that can be called conditionally, unlike all existing hooks. When passed a Promise it integrates with Suspense — suspending the component until the promise resolves. When passed a Context it reads the nearest provider value.
📦 Streaming data from Server → Client
// server component (app/page.tsx) import { Message } from "./message"; export default function Page() { // Promise passed as prop — NOT awaited here const msgPromise = fetchMessage(); return ( <Suspense fallback={<p>Loading…</p>}> <Message messagePromise={msgPromise} /> </Suspense> ); } // client component (message.tsx) "use client"; import { use } from "react"; export function Message({ messagePromise }) { // 🛑 suspends until resolved const msg = use(messagePromise); return <p>{msg}</p>; }
🎨 Conditional Context reading
import { use } from "react"; import { ThemeContext } from "./theme"; function Heading({ children }) { // ✅ use() can be called after an early return if (!children) return null; // useContext() would crash here due to hook rules const theme = use(ThemeContext); return ( <h1 style={{ color: theme.color }}> {children} </h1> ); }
use(). Every render creates a new Promise, which causes an infinite loop. Always create Promises in Server Components or stable top-level caches and pass them down as props.Stable in React 19 — zero-bundle-size components and secure server-side mutations.
Server Components render entirely on the server (or at build time) and send plain HTML or the RSC payload to the client. They can await any async data source directly — no useEffect, no client-side fetch.
app/products/page.tsx — Server Component by default
export default async function ProductsPage() { // Fetch runs on the server — never ships to the browser const products = await db.product.findMany(); return ( <ul> {products.map(p => ( <li key={p.id}>{p.name} — {p.price}</li> ))} </ul> ); }
"use server" directive for Server Components. That directive marks Server Actions only."use server")Server Actions let Client Components call async functions that execute exclusively on the server. The framework automatically creates a reference, serialises arguments, and routes the RPC call.
Server Action + Client Component
// actions.ts "use server"; export async function deletePost(id: string) { await db.post.delete({ where: { id } }); revalidatePath("/posts"); } // PostCard.tsx — Client Component "use client"; import { deletePost } from "./actions"; export function PostCard({ post }) { return ( <div> <h2>{post.title}</h2> {/* Calling deletePost hits the server, never the client bundle */} <button onClick={() => deletePost(post.id)}> Delete </button> </div> ); }
Quality-of-life upgrades that reduce boilerplate throughout your component tree.
ref as a propFunction components now receive ref as a plain prop. No more wrapping in forwardRef.
❌ React 18
const MyInput = forwardRef( function MyInput({ placeholder }, ref) { return ( <input ref={ref} placeholder={placeholder} /> ); } );
✅ React 19
function MyInput({ placeholder, ref }) { return ( <input ref={ref} placeholder={placeholder} /> ); }
<Context> as a provider❌ React 18
<ThemeContext.Provider value="dark"> {children} </ThemeContext.Provider>
✅ React 19
<ThemeContext value="dark"> {children} </ThemeContext>
Ref callbacks can now return a cleanup function. React calls it when the element is removed from the DOM — just like an effect cleanup.
Ref callback with cleanup
<input ref={(el) => { // el is assigned → setup const observer = new ResizeObserver(handleResize); observer.observe(el); // React calls this when the element unmounts return () => observer.disconnect(); }} />
useDeferredValue — initial valueYou can now pass a second argument to useDeferredValue as the value to use on the very first render, before the deferred re-render fires.
Initial value prevents flicker on first render
function Search({ query }) { // First render: value = "" (blank, no flicker) // Subsequent: value updates lazily in background const deferredQuery = useDeferredValue(query, ""); return <Results query={deferredQuery} />; }
Render <title>, <meta>, and <link> from anywhere in the tree — React hoists them to <head> automatically.
Previously you needed react-helmet or framework-specific APIs. In React 19 these tags work natively with Client rendering, Streaming SSR, and Server Components.
Metadata co-located with component
function BlogPost({ post }) { return ( <article> {/* React hoists these three into <head> */} <title>{post.title}</title> <meta name="author" content={post.author} /> <link rel="canonical" href={post.url} /> <h1>{post.title}</h1> <p>{post.body}</p> </article> ); }
Co-locate stylesheets and async scripts with the components that need them — React deduplicates and orders them automatically.
precedencePass a precedence prop to a <link rel='stylesheet'> and React handles insertion order, deduplication, and Suspense integration (the page won't reveal until the sheet is loaded).
Stylesheet precedence controls load order
function Dashboard() { return ( <Suspense fallback="Loading…"> {/* precedence controls insertion order */} <link rel="stylesheet" href="/base.css" precedence="low" /> <link rel="stylesheet" href="/theme.css" precedence="high" /> <main>…</main> </Suspense> ); }
Render <script async src='…' /> anywhere in the tree. React loads each unique src only once, regardless of how many components render it.
Same script, two components — one load
// Both components render the same widget script, // but the browser loads it only once. function MapWidget() { return ( <> <script async src="https://example.com/map.js" /> <div id="map" /> </> ); } function App() { return ( <> <MapWidget /> <MapWidget /> {/* no duplicate script tag */} </> ); }
New imperative APIs from react-dom let you hint the browser about upcoming resources early — before any component even renders.
Browser resource hints from React
import { prefetchDNS, // resolve DNS for a host early preconnect, // open a TCP connection early preload, // fetch font / style / script early preinit, // fetch AND execute a script early } from "react-dom"; function App() { prefetchDNS("https://fonts.googleapis.com"); preconnect("https://cdn.example.com"); preload("https://cdn.example.com/hero.jpg", { as: "image" }); preinit("https://cdn.example.com/analytics.js", { as: "script" }); // … }
Fewer duplicate logs, richer diffs, and new lifecycle callbacks for error boundaries.
React 18 fired multiple overlapping warnings for a single hydration mismatch. React 19 collapses them into one structured message with a unified diff:
React 19 console output — single structured message
Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. <App> <span> + Client ← client sent this - Server ← server sent this at throwOnHydrationMismatch …
createRoot and hydrateRoot now accept three callbacks so you can route errors to an error-tracking service with full context.
Three error lifecycle callbacks
createRoot(document.getElementById("root"), { onCaughtError(error, errorInfo) { // Error caught by an Error Boundary Sentry.captureException(error, errorInfo); }, onUncaughtError(error, errorInfo) { // Error NOT caught — app will likely crash Sentry.captureException(error, errorInfo); }, onRecoverableError(error, errorInfo) { // React self-recovered (hydration mismatch, etc.) console.warn("Recoverable:", error); }, }).render(<App />);
React 19 passes all tests on Custom Elements Everywhere — properties vs attributes are now handled correctly on both client and server.
Client-side rendering
Props matching a property on the Custom Element instance are set as properties. All others become attributes.
Server-side rendering
Primitive props (string, number, true) render as attributes. Objects, functions, and false are omitted.
Custom element props in React 19 ✅
<my-video-player src="/video.mp4" // → attribute (string) volume={0.8} // → property on client, attribute on server onPlaybackEnd={fn} // → property (function, SSR-omitted) autoplay // → attribute (boolean true) />
prerender and prerenderToNodeStream — static-site generation that actually waits for all data.
The existing renderToString didn't wait for suspended data. The new static APIs do — making them ideal for SSG build steps.
prerender — waits for ALL suspended data
import { prerender } from "react-dom/static"; export async function GET() { // Waits for ALL suspended data before resolving const { prelude } = await prerender(<App />, { bootstrapScripts: ["/main.js"], }); return new Response(prelude, { headers: { "content-type": "text/html" }, }); }
Everything new at a glance.
Actions
Async functions in transitions. React manages pending, errors, and form reset.
useActionState
Wrap an Action and get [state, dispatch, isPending] back.
useFormStatus
Read parent <form> pending/data from any child — no prop passing.
useOptimistic
Instantly reflect a mutation in the UI; auto-revert on failure.
use()
Read a Promise or Context conditionally — even after early returns.
Server Actions
"use server" functions called from Client Components via RPC.
ref as prop
Pass ref directly — forwardRef is no longer needed.
<Context> provider
Use <MyCtx value={…}> instead of <MyCtx.Provider>.
Document Metadata
<title>, <meta>, <link> are hoisted to <head> automatically.
Ref cleanup
Ref callbacks can return a cleanup function, like useEffect.
Stylesheet precedence
Manage CSS insertion order and defer renders until loaded.
Custom Elements
Full property/attribute support on client and server.
A running log of every significant release, feature drop, and ecosystem event from February 2025 through February 2026.
Create React App officially deprecated
ecosystemAfter nearly a decade CRA is deprecated. The React team recommends Next.js, React Router, or Expo for new projects. CRA enters maintenance mode.
React 19.1 released
releasePatch release with stability improvements, updated useId prefix from :r: to «r», and critical bug fixes across the runtime and SSR.
React Labs: View Transitions & Activity
experimentalTwo new experimental features ready to test: <ViewTransition> for declarative page animations and <Activity> for state-preserving visibility control. Also previewed: Performance Tracks, Compiler IDE Extension, Automatic Effect Dependencies.
React 19.2 released
releaseShips stable <Activity>, useEffectEvent, cacheSignal, Partial Pre-rendering, Performance Tracks for Chrome DevTools, SSR batching improvements, Web Streams for Node.js, and eslint-plugin-react-hooks v6.
React Compiler v1.0 stable
toolingFirst stable release of React Compiler with automatic memoization, compiler-powered lint rules, SWC plugin support, and default enablement in new Expo/Vite/Next.js projects.
React Foundation announced
governanceIndependent non-profit to steward React's long-term open source development and community health. Meta contributed the React trademark and brand assets.
React Conf 2025
eventHenderson, NV. Keynotes covered React 19.2 features, React Compiler v1.0, React Native 0.82 (New Architecture only), Fragment Refs, Partial Pre-rendering, and Async React patterns.
Critical RSC Security Vulnerability — CVE-2025-55182
securityCVSS 10.0 unauthenticated RCE via malicious deserialization of Server Function payloads. Fixed in v19.0.1, v19.1.2, v19.2.1. Update all react-server-dom-* packages immediately.
Follow-up RSC vulnerabilities patched
securityTwo additional CVEs: Denial of Service (CVE-2025-55184, CVSS 7.5) and Source Code Exposure (CVE-2025-55183, CVSS 5.3). All fixed in the same v19.x.1+ patch releases.
CVE-2026-23864 — DoS follow-up
securityAdditional Denial of Service vulnerability (CVSS 7.5) patched in January 2026. Keep react-server-dom-* packages on latest.
February 14, 2025 — React team officially deprecates the tool that bootstrapped an entire generation of apps.
After nearly a decade, Create React App (CRA) is deprecated as of February 2025. The React team cited no active maintainers and the fact that modern production apps require routing, code splitting, data-fetching coordination, and SSR — all solved more elegantly by frameworks.
New apps
Use Next.js, React Router (framework mode), or Expo
Custom setups
Roll your own with Vite, Parcel, or Rsbuild
Migration guides
Vite, Next.js, React Router, and Expo migration docs published
npx create-react-app.The first patch release in the React 19 series — improved stability and a useId hash update.
React 19.1 focused on hardening the 19.0 features and fixing regressions reported in the wild. The most visible change is a new default prefix for useId-generated IDs, paving the way for View Transition compatibility.
useId prefix updated
Default prefix changed from :r: (19.0) to «r» (19.1) to avoid conflicts with View Transition name requirements.
Bug-fix release
Dozens of fixes across the runtime, SSR streaming, and hydration edge cases reported after the 19.0 stable launch.
Security patch baseline
v19.1.0 and v19.1.1 were later found to contain the RSC RCE vulnerability; v19.1.2 is the safe version.
Upgrade path
Drop-in upgrade: npm install react@19.1 react-dom@19.1 — no API changes required.
April 23, 2025 Labs post — two features ready to try today.
<ViewTransition>A new component that wraps elements you want animated. Three primitives control everything:
<ViewTransition> wraps the elementstartTransition, useDeferredValue, or <Suspense>::view-transition-* pseudo-elementsViewTransition usage
import { ViewTransition } from 'react'; // Navigate → cross-fade automatically <ViewTransition key={url}> {url === '/' ? <Home /> : <Details />} </ViewTransition> // Shared element between pages <ViewTransition name={`video-${id}`}> <Thumbnail /> </ViewTransition>
<Activity>Hide and show parts of the UI while preserving state. Available stable in React 19.2.
mode="visible" — renders normally, effects mountmode="hidden" — unmounts effects, defers updates, saves stateActivity usage
import { Activity } from 'react'; // Before: state lost on unmount {isVisible && <Page />} // After: state preserved when hidden <Activity mode={isVisible ? 'visible' : 'hidden'}> <Page /> </Activity> // Pre-render likely next route in background <Activity mode={url === '/about' ? 'visible' : 'hidden'}> <About /> </Activity>
Also in development (from the same Labs post)
React Performance Tracks
Custom Chrome DevTools lanes — shipped in 19.2
Compiler IDE Extension
LSP-powered hints, CodeLens for effect deps
Automatic Effect Dependencies
Compiler infers & inserts deps — no array typing
Fragment Refs
ref on <> pointing to multiple DOM nodes
Gesture Animations
Swipe / continuous gesture view transitions
Concurrent Stores
use(store) replacing useSyncExternalStore
<ViewTransition> PatternsThree concepts control View Transitions: "what" (wrap with <ViewTransition>), "when" (startTransition, useDeferredValue, or <Suspense>), and "how" (CSS ::view-transition-* pseudo-selectors).
Wrap route content in <ViewTransition> to get a default cross-fade on navigation.
import { ViewTransition } from 'react';
// Wrap the page that changes on navigation
export default function App() {
const { url } = useRouter();
return (
// "WHAT" to animate
<ViewTransition key={url}>
{url === '/' ? <Home /> : <Details />}
</ViewTransition>
// "WHEN": startTransition triggers the animation
);
}startTransitionNavigation, state toggle
useDeferredValueLive search, list filter
<Suspense>Fallback → content swap
Available in react@canary. Next.js 16 Canary supports ViewTransition via @vitejs/plugin-rsc or the canary channel.
The headline release of 2025: stable Activity, useEffectEvent, Performance Tracks, Partial Pre-rendering, and more.
useEffectEventSeparates "event-like" logic from synchronization logic inside Effects. An Effect Event always sees the latest props and state without being listed as a dependency — preventing unwanted Effect re-runs.
useEffectEvent — stable dep array without stale values
import { useEffect, useEffectEvent } from 'react'; function ChatRoom({ roomId, theme }) { // ✅ onConnected always sees latest theme // but does NOT go in the dependency array const onConnected = useEffectEvent(() => { showNotification('Connected!', theme); }); useEffect(() => { const connection = createConnection(roomId); connection.on('connected', () => onConnected()); connection.connect(); return () => connection.disconnect(); }, [roomId]); // ← only roomId, NOT theme }
<Activity> — now stableOfficially stable in React 19.2 with two modes: mode="visible" (normal render, effects mount) and mode="hidden" (effects unmounted, updates deferred, state preserved). SSR optimization: hidden Activity is excluded from the initial HTML response and rendered client-side at low priority.
cacheSignal (Server Components only)Lets you know when a cache() entry lifetime is over so you can abort in-flight requests. Useful for passing to fetch(url, { signal: cacheSignal() } ) inside cached Server Component functions.
cacheSignal — abort stale cache requests
import { cache, cacheSignal } from 'react'; const fetchUser = cache(async (id) => { // Abort the request if the cache result is no longer used return fetch(`/api/users/${id}`, { signal: cacheSignal() }); });
React 19.2 adds custom tracks to Chrome DevTools Performance profiles. Two tracks are exposed:
⚛ Scheduler track
Shows work priorities (blocking, transition), events that triggered updates, and when renders happened. Visualizes how React splits work across priorities.
⚛ Components track
Shows the component tree being rendered or running effects. Labels include Mount, Blocked, and Update to reveal time spent on each component.
useEffectEvent — React 19.2useEffectEvent separates "event-like" code from synchronization code. The notification always shows the current theme, yet changing the theme does not reconnect to the room.
No events yet…
<Activity> — React 19.2Switch between channels. With <Activity>, each panel's state (chat history, input text) is preserved when hidden — even though the component is unmounted. Navigate back and everything is still there.
#general Channel
💬 Welcome to the blue channel!
<Activity> (conditional rendering only), state would be lost.Pre-render the static shell of an app ahead of time (serve from CDN), then resume rendering dynamic content into it later. Uses a two-step API:prerender() with an AbortController, then resume() / resumeAndPrerender().
PPR — static shell + dynamic resume
// Step 1 — Pre-render static shell const { prelude, postponed } = await prerender(<App />, { signal: controller.signal, }); await savePostponedState(postponed); // Step 2 — Resume with dynamic data (server-side) const stream = await resume(<App />, await getPostponedState(req)); // Or for SSG: resume back to static HTML const { prelude } = await resumeAndPrerender(<App />, postponedState);
Batching SSR Suspense reveals
Suspense boundaries now batch content reveals during streaming SSR for a short time, aligning with client-render behavior and enabling smoother ViewTransition animations.
Web Streams for Node.js
renderToReadableStream and prerender now available for Node.js. New resume / resumeAndPrerender APIs for Web Streams. (Still recommend Node Streams for performance.)
eslint-plugin-react-hooks v6
Flat config by default in the recommended preset. Opt-in compiler-powered rules. Legacy config users: switch to recommended-legacy.
useId prefix updated to _r_
Changed from «r» (19.1) to _r_ so generated IDs are valid for view-transition-name and XML 1.0 names.
A build-time tool that automatically memoizes your React components and hooks — already installed in this project.
babel-plugin-react-compiler@^1.0.0 is installed in this project (see package.json). Every component and hook in this app is already automatically memoized.❌ Before — manual memoization
// You had to remember to memoize const expensiveValue = useMemo(() => { return computeExpensiveValue(a, b); }, [a, b]); const handleClick = useCallback(() => { doSomething(a, b); }, [a, b]); const MemoChild = React.memo(({ data }) => ( <div>{data}</div> ));
✅ After — compiler handles it
// Write plain code. Compiler memoizes // exactly what needs memoizing — and // can memoize after early returns which // useMemo/useCallback cannot do. const expensiveValue = computeExpensiveValue(a, b); const handleClick = () => { doSomething(a, b); }; // No React.memo wrapper needed function Child({ data }) { return <div>{data}</div>; }
⚡ 12% faster loads
Meta Quest Store saw up to 12% improvement in initial load and cross-page navigations, and 2.5× faster interactions.
🔍 Compiler-powered linting
New lint rules in eslint-plugin-react-hooks ship with the compiler: set-state-in-render, set-state-in-effect, refs safety checks.
🔄 Backwards compatible
Works with React 17+. Add react-compiler-runtime as a dependency and set a target in compiler config for pre-19 projects.
🏗️ Default in new projects
Partnered with Expo (SDK 54+), Vite, and Next.js — enabled by default in create-expo-app, create-vite, create-next-app.
⚙️ SWC support (experimental)
Experimental SWC plugin dramatically speeds up Next.js builds when React Compiler is enabled. Requires Next.js 15.3.1+.
🔒 Pin exact version
If test coverage is limited, pin babel-plugin-react-compiler to an exact version (1.0.0) rather than a semver range to avoid unexpected memoization changes.
useMemo / useCallback as an escape hatch when you need precise control (e.g., as effect dependencies).An independent non-profit to steward React's long-term open source development beyond Meta.
Announced during React Conf 2025, the React Foundationis a new governance structure that separates React's open source stewardship from Meta. Meta contributed the React trademark and brand assets to the Foundation.
Open source stewardship
Independent governance ensures React's development continues to serve the wider community, not just Meta's internal needs.
Trademark & brand
Meta has contributed the React trademark and brand assets to the Foundation, formalizing community ownership.
Community health
The Foundation's mission includes long-term community sustainability, contributor support, and ecosystem coordination.
A series of vulnerabilities were disclosed and patched in react-server-dom-* packages. If you use RSC, update now.
CVE-2025-55182 — CVSS 10.0 (Critical)
Unauthenticated Remote Code Execution via React Server Functions. Disclosed December 3, 2025.
An unauthenticated attacker could craft a malicious HTTP request to any Server Function endpoint. When deserialized by React, it achieves remote code execution on the server. Apps that use React Server Components or Server Functions are affected, even if they don't define explicit endpoints. Reported by Lachlan Davidson via Meta Bug Bounty.
Affected packages
react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack
Affected versions
19.0.0, 19.1.0, 19.1.1, 19.2.0
Fixed in
v19.0.1, v19.1.2, v19.2.1+
CVE-2025-55184
High (CVSS 7.5)
Denial of Service
Dec 11, 2025
Fix: Same v19.x.1+ patch releases
CVE-2025-55183
Medium (CVSS 5.3)
Source Code Exposure
Dec 11, 2025
Fix: Same v19.x.1+ patch releases
CVE-2026-23864
High (CVSS 7.5)
Denial of Service
Jan 26, 2026
Fix: Latest react-server-dom-* packages
Affected frameworks — update commands
Next.js
npm install next@latest
See nextjs.org/blog for your release line
React Router (RSC)
npm install react@latest react-dom@latest react-server-dom-webpack@latest
Only if using unstable RSC APIs
Waku
npm install waku@latest react@latest react-dom@latest react-server-dom-webpack@latest
Expo
See expo.dev/changelog
Mitigation guide published
React 19.2 — Latest stable
Released October 1, 2025 · React Compiler v1.0 is installed in this project
Install latest
npm install react@latest react-dom@latestAdd Compiler
npm install -D --save-exact babel-plugin-react-compiler@latestTry canary
npm install react@canary react-dom@canarySee the Upgrade Guide · React 19.2 Blog Post · React Compiler Docs