React 19 shipped in December 2024. The headline change is that async work, especially form submissions and data mutations, is now built into React instead of being something you wire up with useState and useEffect. Since then, React 19.2 (October 2025) and 19.3 (September 2026) have added more. This guide walks through what is new in React 19, with code that actually matches the current API.
React 19 Features: What Is New
React 19 adds Actions (async functions you pass to transitions and forms), the hooks useActionState, useOptimistic and useFormStatus, the use API for reading promises and context, ref as a regular prop, <Context> as a provider, built-in <title> and <meta> support, better hydration error messages, and full custom element support. Server Components and Server Actions are stable.
| Feature | What it replaces |
|---|---|
Actions and useActionState | Manual isPending, error and onSubmit state |
useOptimistic | Hand-rolled optimistic state and rollback |
use | useEffect plus state for reading promises |
ref as a prop | forwardRef |
<Context> provider | <Context.Provider> |
<title>, <meta>, <link> in components | react-helmet style libraries |
1. Actions: Async Work Built Into React
An Action is an async function used in a transition. React tracks the pending state, handles errors through error boundaries, and batches the resulting updates. In practice you meet Actions most often through forms: pass a function to the action prop and React calls it with the FormData on submit and resets uncontrolled fields when it succeeds.
function UpdateNameForm() {
async function updateName(formData) {
await api.updateUser({ name: formData.get('username') });
}
return (
<form action={updateName}>
<input type="text" name="username" placeholder="Enter your name" />
<button type="submit">Update</button>
</form>
);
}There is no 'use action' directive. Plain functions passed to action run on the client. The 'use server' directive marks a Server Action, which runs on the server (more on that below).
2. useActionState: Pending, Error and Result in One Hook
useActionState wraps an action and gives you the last result, a wrapped action to pass to the form, and a pending flag.
import { useActionState } from 'react';
async function saveName(previousState, formData) {
const error = await api.updateUser({ name: formData.get('username') });
if (error) return error;
return null;
}
function NameForm() {
const [error, submitAction, isPending] = useActionState(saveName, null);
return (
<form action={submitAction}>
<input type="text" name="username" />
<button type="submit" disabled={isPending}>
{isPending ? 'Saving...' : 'Save'}
</button>
{error && <p>{error}</p>}
</form>
);
}The signature is useActionState(action, initialState), and it returns [state, formAction, isPending]. Your action receives the previous state as its first argument and the FormData as its second.
A related hook, useFormStatus from react-dom, lets a child component (like a shared submit button) read the pending state of the parent <form> without prop drilling.
3. useOptimistic: Instant UI While the Server Catches Up
useOptimistic shows a temporary value while an async action runs, then snaps back to the real state when it finishes or fails.
import { useOptimistic } from 'react';
function TaskList({ tasks, addTaskAction }) {
const [optimisticTasks, addOptimisticTask] = useOptimistic(
tasks,
(currentTasks, newTask) => [...currentTasks, { ...newTask, sending: true }]
);
async function formAction(formData) {
const name = formData.get('name');
addOptimisticTask({ id: crypto.randomUUID(), name });
await addTaskAction(name);
}
return (
<form action={formAction}>
<ul>
{optimisticTasks.map((task) => (
<li key={task.id}>
{task.name} {task.sending && <small>(sending)</small>}
</li>
))}
</ul>
<input name="name" />
<button type="submit">Add</button>
</form>
);
}The second argument is a reducer that merges the optimistic value into the current state. Call the returned function inside an action or transition.
4. The use API: Read Promises and Context in Render
use reads a resource during render. Given a promise, it suspends until the promise resolves, so you wrap the component in <Suspense>. Unlike hooks, you can call use conditionally.
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map((c) => <p key={c.id}>{c.text}</p>);
}
export function Page({ commentsPromise }) {
return (
<Suspense fallback={<p>Loading comments...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}Create the promise outside the component that reads it (for example in a Server Component or a cache), not inside the render that calls use, or you will start a new request on every render.
5. ref as a Prop, and Simpler Context
Function components can now receive ref as an ordinary prop, so forwardRef is no longer needed for new code. Ref callbacks can also return a cleanup function, which React runs when the element unmounts.
function MyInput({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />;
}Context got a small cleanup too. You can render <ThemeContext> directly as the provider instead of <ThemeContext.Provider>:
const ThemeContext = createContext('light');
function App({ children }) {
return <ThemeContext value="dark">{children}</ThemeContext>;
}forwardRef and Context.Provider still work for now, so migration can be gradual.
6. Document Metadata, Stylesheets and Preloading
React 19 lets you render <title>, <meta> and <link> tags anywhere in your component tree and hoists them into the document <head>. Stylesheets can declare a precedence so React orders them correctly and avoids unstyled flashes, and async <script> tags are deduplicated. New preload and preinit functions in react-dom let you hint resources early.
function BlogPost({ post }) {
return (
<article>
<title>{post.title}</title>
<meta name="author" content="Alex" />
<h1>{post.title}</h1>
</article>
);
}If you use a framework like Next.js, keep using its metadata API. The built-in tags matter most for plain React and Vite apps.
7. Server Components, Server Actions and Custom Elements
Server Components are stable in React 19, and frameworks like Next.js build on them. A Server Component is an async function that runs only on the server and sends its rendered output to the client:
// app/profile.jsx (a Server Component)
export default async function Profile({ userId }) {
const user = await fetchUserData(userId);
return <div>{user.name}</div>;
}Server Actions are async functions marked with 'use server' that the client can call like normal functions:
'use server';
export async function updateName(formData) {
await db.user.update({ name: formData.get('name') });
}React 19 also passes the Custom Elements Everywhere tests, so web components receive props as properties and events work as expected.
React 19 also improved error reporting. Hydration mismatches now produce a single clear diff instead of several vague errors, and uncaught and caught errors have new onUncaughtError and onCaughtError root options.
8. What 19.2 and 19.3 Added
- React 19.2 (October 2025):
<Activity>for hiding and restoring parts of the UI while keeping state,useEffectEventfor effect callbacks that always see fresh values, and React Performance Tracks in browser profilers. - React 19.3 (September 2026): View Transitions, Fragment Refs and Trusted Types support, among other changes, per the React blog.
One more thing to know: a critical remote code execution vulnerability in React Server Components was disclosed in December 2025 and fixed in 19.0.1, 19.1.2 and 19.2.1. If you run Server Components, make sure you are on a patched version.
Migrating to React 19
- Read the official React 19 upgrade guide for removed APIs.
- Update
reactandreact-dom, and bump libraries that peer-depend on React 18. - Replace
forwardRefandContext.Providerwhen convenient. They still work. - Move form submissions to Actions and
useActionStatewhere they replace manual pending and error state. - Turn on the React Compiler, which pairs well with the new hooks because you can stop hand-writing memoization.
npm install react@latest react-dom@latestIf you are using Next.js, Next.js 16.3 builds on these features. For a comparison with another reactive model, see Svelte 5 vs React.
Frequently Asked
What is new in React 19? Actions, useActionState, useOptimistic, useFormStatus, the use API, ref as a prop, <Context> as a provider, built-in document metadata, better hydration errors, and full custom element support.
Is React 19 stable? Yes. It has been stable since December 2024, and the 19.x line has continued with 19.2 and 19.3.
Do I still need forwardRef in React 19? Not for new code. Function components receive ref as a prop. forwardRef still works, so you can migrate gradually.
What is the difference between useActionState and useOptimistic? useActionState tracks the result and pending state of an action. useOptimistic shows a temporary value while an action is still running. They are often used together in forms.