# React 19: New Features, Improvements and Best Practices

React 19, released in December 2024, made async work a first-class part of React with Actions, new hooks, ref as a prop and built-in document metadata. This guide covers what is new in React 19, working code for each feature, and what the 19.2 and 19.3 releases added since.

Tags: react, javascript, web-development
Published: 2025-01-06 (updated 2026-10-22)


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.

```jsx
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.

```jsx
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.

```jsx
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.

```jsx
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.

```jsx
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>`:

```jsx
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.

```jsx
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:

```jsx
// 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:

```jsx
'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, `useEffectEvent` for 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

1. Read the [official React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) for removed APIs.
2. Update `react` and `react-dom`, and bump libraries that peer-depend on React 18.
3. Replace `forwardRef` and `Context.Provider` when convenient. They still work.
4. Move form submissions to Actions and `useActionState` where they replace manual pending and error state.
5. Turn on the [React Compiler](/blog/the-react-compiler-is-here-say-goodbye-to-usememo-and-usecallback/), which pairs well with the new hooks because you can stop hand-writing memoization.

```bash
npm install react@latest react-dom@latest
```

If you are using Next.js, [Next.js 16.3](/blog/nextjs-16-3-instant-navigations-cache-components/) builds on these features. For a comparison with another reactive model, see [Svelte 5 vs React](/blog/svelte-5-vs-react-2026/).

## 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.
