# React 19 use() and Actions: The New Async Model

> A practical tour of React 19's use() hook and Actions: how useActionState and useOptimistic replace manual loading, error, and pending state boilerplate.

URL: https://www.matthewswong.com/en/blog/react-19-use-hook-actions-guide/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-07-03
- Updated: 2026-07-03
- Category: Web Dev

React 19 quietly rewrites the rules for how components talk to the network. For years, fetching data and submitting forms in React meant the same ritual: a useState for the result, a useState for the loading flag, a useState for the error, and a useEffect or a click handler to wire them all together. React 19 collapses most of that boilerplate into two ideas, the use function and Actions, plus a small family of hooks built around them.

This guide walks through what use actually does, how Actions change form and button handling, and where useActionState and useOptimistic fit. The goal is not to memorize an API surface but to understand the mental model well enough to know which tool to reach for the next time you build a data-driven feature, whether that is a profile form, a comment box, or a paginated list.

## What use() Actually Does

Despite the hook-like name, use is not a Hook in the strict sense, which is exactly what makes it powerful. It can be called inside conditionals, loops, and after early returns, something the Rules of Hooks normally forbid. What use does is simple to state and easy to misuse: hand it a Promise or a Context object, and it returns the resolved value, suspending the component while a Promise is still pending. If the promise rejects, the nearest error boundary catches it, so you no longer need a try and catch around every fetch.

Never create the Promise you pass to use directly inside the component body on every render. A fresh Promise on each render defeats Suspense and can trigger an infinite render loop. Create the Promise once, in a parent component, a route loader, or a small cache keyed by URL, then pass the same Promise instance down as a prop.

## Suspense, Not useEffect, Owns the Loading State

The practical shift with use is where the loading state lives. Instead of a component tracking its own isLoading flag, the parent starts the fetch and passes the Promise down, and a Suspense boundary wrapping the consumer renders a fallback until the data resolves. This mirrors how server frameworks like Next.js already stream data, and it means the same mental model now works for client components reading Context conditionally or for Server Components streaming a Promise straight to the client. Nested Suspense boundaries also let you stream a page in pieces, showing the shell immediately while slower sections, such as a comments list or a recommendations panel, resolve independently without blocking the rest of the layout.

### Reading Context Without the Old Restrictions

Because use can run inside an if statement, you can read a theme or locale Context only when a certain branch of the render actually needs it, instead of calling useContext unconditionally at the top of the function and hoping the value is not needed for other branches. This is a small change, but it removes a class of workarounds where developers used to split a component in two purely to satisfy the old Rules of Hooks around conditional Context reads.

> If a fetch never changes for the lifetime of a component, memoize the Promise in a Map keyed by the request parameters. That single line of caching is usually the difference between use working cleanly and use causing a suspend loop.

## Actions Turn Async Functions Into a First-Class UI Primitive

An Action in React 19 is just an async function passed to a form's action prop, a button's formAction prop, or wrapped with startTransition. What makes it special is what React does around it automatically: pending state is tracked without an extra useState, the previous form values stay intact if the action throws, and errors can be caught by an error boundary. useActionState packages this pattern into a single hook that returns the current state, a wrapped action to call, and an isPending flag, effectively behaving like useReducer for side effects triggered by user interaction. The optional permalink argument also matters for progressive enhancement: if JavaScript has not finished loading yet and a user submits the form anyway, the browser can still navigate to that URL as a real HTML form submission instead of failing silently.

## useOptimistic: Instant Feedback That Reverts on Failure

Optimistic UI used to require careful manual bookkeeping: update local state immediately, remember the previous value, then roll back if the request fails. useOptimistic formalizes this pattern. It takes the current confirmed value and returns an optimistic value plus a setter. Call the setter synchronously inside an Action before the awaited request resolves, and React shows the optimistic value immediately, automatically reverting to the real state once the Action finishes, whether it succeeds or throws.

useOptimistic only reverts automatically when the Action that triggered it actually completes or throws. If you call the setter outside an Action, or if the surrounding async function never resolves because of an unhandled promise, the optimistic value can appear stuck on screen indefinitely.

## Choosing the Right Hook for the Job

With four related APIs now available, it helps to compare them side by side rather than picking one from memory. The table below lists the practical purpose of each and the situation where it is the clear choice over the alternatives. Note that useFormStatus is intentionally not a replacement for useActionState; it exists specifically so a deeply nested submit button can read the pending status of its parent form without that status being threaded down as a prop.

API

Purpose

Use it when

Unwraps a Promise or reads Context during render, suspending until ready

Reading data passed down as a cached Promise, or reading Context conditionally

Wraps an async Action and tracks its result, error, and pending flag

Handling a form submission or button click that calls a server mutation

Shows an immediate UI update that reverts automatically if the Action fails

Likes, follows, chat messages, or any interaction where instant feedback matters more than certainty

Reads the pending status of the nearest parent form from a child component

A submit button or spinner lives in a separate component from the form itself

## A Practical Migration Checklist

Adopting these APIs works best as an incremental migration rather than a rewrite. Start with the forms and buttons that already have the most manual pending and error state, since that is where the boilerplate reduction is most visible and the risk is lowest. There is no requirement to touch data fetching and form handling in the same pass; treating them as two separate migrations keeps each pull request small enough to review carefully and roll back independently if something regresses.

- Replace a manual isSubmitting useState plus try catch block around a form handler with useActionState first, since it is the safest and most isolated change.
- Move any fetch call that currently lives in a useEffect with a loading flag into a cached Promise read with use, wrapped in a Suspense boundary with a real fallback, not a blank screen.
- Add useOptimistic only to interactions where users expect instant feedback, such as toggling a favorite or posting a comment, not to every mutation in the app.
- Wrap every new use() consumer in an error boundary, since a rejected Promise now surfaces through the boundary instead of a silently caught error inside a useEffect.

Teams that migrated form-heavy pages first reported the biggest drop in boilerplate, because useActionState alone typically removes three separate useState calls per form.

> Actions and use compose well with React Query or SWR too. Nothing about this model requires abandoning your existing data layer; use can unwrap a Promise that a query library already produced and cached.

## Sources

- use – React reference documentation
- useActionState – React reference documentation
- useOptimistic – React reference documentation
- React v19 release notes – react.dev blog

## Source links

- https://react.dev/reference/react/use
- https://react.dev/reference/react/useActionState
- https://react.dev/reference/react/useOptimistic
- https://react.dev/blog/2024/12/05/react-19

## FAQ

### What does the React 19 use() hook actually do?

The use function reads the resolved value of a Promise or the current value of a Context during render. When given a pending Promise, the component suspends until it resolves, and a wrapping Suspense boundary shows a fallback in the meantime. Unlike other hooks, it can be called conditionally or inside loops, which makes it more flexible than useContext for reading Context.

### Why does my component suspend forever when I use the use() hook?

This almost always happens because a new Promise is created on every render instead of being cached. React expects the same Promise instance across re-renders so it can track whether it has resolved. Fix it by creating the Promise once outside the render path, such as in a parent component, a route loader, or a Map keyed by request parameters, and passing that same instance down as a prop.

### How is useActionState different from managing state manually?

useActionState wraps an async Action function and automatically tracks its result, error, and a pending flag, replacing what used to require several separate useState calls plus manual try catch logic. It also integrates directly with the form action prop, so React resets the pending state after the action settles without extra code.

### Can I use useOptimistic outside of a form submission?

Yes. useOptimistic works with any Action, not just form submissions, including button clicks wrapped in startTransition or calls inside useActionState. The requirement is that the setter is called inside an Action, since the optimistic value only reverts automatically once that Action completes or throws.

### Do I need to rewrite my existing data fetching to adopt React 19 Actions?

No, adoption can be incremental. Forms and buttons with the most manual pending and error state boilerplate are the best starting point because the reduction is immediately visible and the change is isolated. Actions and use also compose with existing data libraries like React Query, since use can simply unwrap a Promise that library already produces.

---

- HTML version: https://www.matthewswong.com/en/blog/react-19-use-hook-actions-guide/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/react-19-use-hook-actions-guide/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
