Code Your Path Coding School

New React 19 Actions: Guide to useOptimistic, useActionState, and useFormStatus Async Hooks with Code Examples

React 19 Actions

Table of Contents

Uncover new React 19 Actions: useOptimistic, useActionState, and useFormStatus hooks — all the latest features to control async forms in JavaScript!

New useFormStatus, useFormState and useOptimistic hooks can work together with Actions, Transitions, and Suspense, making a more powerful UI out of the box.

If you need more control over your form component — React’s got you! This post teaches you how to display UI feedback, control pending state, and submit `<form>`.

These will soon be available in the main React release channel with v19.

What Are New React 19 Actions?

The updates we are discussing today— useOptimistic hook and DOM `<form>` actions: useFormStatus and useActionState hooks — were available only with Next.js and React-Hook-Forms.

React 19 changes how we build forms with new features and moves them from React Canary to the main release channel. (Read my other posts about Best React 19 Features and React 19 Compiler)

New hooks control the `<form>` DOM elements and give all the form info — state (form data), action, action state (pending or not), and other details — letting you keep your UI responsive and interactive when processing data.

New hooks can be used on the client and server side, too!

Whether you’re building client-side applications with Vite, CRA, or server-side applications with Next.Js or Remix.Js, React 19 will control form smoothly in a simple way.

New Look at Client And Server Side `<form>` with React 19 Actions

React 19 adds a powerful way to work with data and mutations for custom forms. These new “actions” let you pass custom functions directly to `<form>` DOM elements.

Here’s an example:

<form action={search}
  //...Form fields
</form>

React 19 allows you to define the action (synchronous or asynchronous search function) in the client and server components with the new ‘use server’ directive.

Action can be URL or Function.

If the action is a URL — the form behaves like a traditional HTML/DOM `<form`>.

If you pass a function — it will handle the `<form>` submission. The function can be asynchronous, taking the form data as its argument.

Form elements in React 19 natively accept actions (functions), allowing you to specify exactly what the form should do with the entered information. React 19 directly supports “action” (functions) as props of `<form>,` `<input>,` and `<button>` DOM elements — these let you submit forms with your custom functions!

Simplest example for `<form>` with React 19:

When you hit the button and submit the `<form>`, the login function receives the formData and processes it asynchronously.

Form Actions Simplify Development Process in React 19 Web Components

Traditionally, an HTML form would make a REST call on submit, requiring a URL.

When calling an action, React 19 will NEVER send the function itself to the client. Instead, React dispatch a handler, which triggers the SERVER to call the function. This setup has many benefits

  1. Reduced Bundle Size — Since React doesn’t send the script to the client, the overall bundle size is smaller (faster load times)
  2. No JavaScript Required — `<Forms>` can function even with the turned-off JavaScript in the browser
  3. Increased Security — Proxing everything through the server gives security, protecting the form data from client-side attacks

You can use traditional methods, even within the same React.Js app. This ensures that existing workflows continue to work while you gradually migrate over to new features.

You also can reassign (override) the action prop with a formAction attribute on <button>, <input type=”submit”>, or <input type=”image”> elements.

What Is the Purpose of Using useOptimistic Hook in Data Handling and Loading?

The useOptimistic hook ensures that your app stays responsive and interactive with the user while communicating with the server.

Suppose you are changing your name within your social media app, and instead of waiting for the server to confirm the change, you see your new name IMMEDIATELY! This is an example of optimistic UI updates — a pattern showing the final state of data while an asynchronous request is still underway.

You must confirm to a user that an operation was completed successfully. For example, you could indicate a form submission with a grey checkmark (✓) and turn that green when the response comes back.

Here’s how the new hook looks:

const [optimisticState, setOptimisticState] = useOptimistic(state, updateFn);

API-wise, useOptimistic works similarly to useState.

It takes the default state (state) and a function (updateFn) in which you update the state optimistically. 

useOptimistic returns optimisticMessages with the current state and setOptimisticMessage function to set a new state.

When you add a new item — it shows up immediately!

If the server rejects the request — hide and re-render the items. If the request is successful, keep them stay put. This way, users see (hopefully) accurate data immediately, eliminating the waiting period for server responses.

However, most React applications don’t require such a high level of feedback, as the modern network has very low delays (aim at 50 to 150 milliseconds) — already beyond satisfactory for a smooth UX.

For now, you might just want to use useOptimistic from React-Query. It’s a little clunky, but generally, it feels more reliable than the still-evolving React. Suspense pattern. (Read more about suspense in my 5 Real-World Examples With React.Suspense)

useOptimistic Hook — Example With Reducer

You can leverage create, update, and delete operations utilizing reducers to handle different actions.

The taskReducer function is a reducer that handles different actions (add, delete, and update) to manipulate the tasks list. Depending on the action you provide, it will either remove a task, update a task, or add a new task to the state array. Here, I use a switch statement to handle data with different actions:

// Define the reducer function
function taskReducer(state, { action, task }) {
  switch (action) {
    case "delete":
      return state.filter(({ id }) => id !== task.id);
    case "update":
      return state.map((stateTask) => (stateTask.id === task.id ? task : stateTask));

    case "add":

      return [...state, task];

    default:

return state
  }
}

// Use the reducer function in the useOptimistic hook
const [optimisticTasks, setOptimisticTasks] = useOptimistic(tasks, taskReducer);

Pass the taskReducer to the useOptimistic hook as its second argument accepts a reducer to handle the state changes depending on actions.

useActionState Hook — Simplifying Form Handling in React 19 And Server Components

React 19 now also supports data submission with new `<form>` “action” — useActionState hook. 

In React Canary releases, this hook was initially known as ReactDOM.useFormState. It was later renamed to React.useActionState and useFormState was deprecated.

Use the useActionState hook to update the state based on the result of a form action, simplifying the handling of forms in your applications.s

const [state, formAction] = useActionState(fn, initialState, permalink?);

This hook takes a function as its first parameter and returns a tuple with the form state and an action that you need to pass to the action prop.

Passing the action to the form, you bind the form to the state returned by the hook.

import { useActionState, useState } from "react";
async function updateName(prevState, formData) {
  const newName = formData.get("userName");
  alert(`New name: ${newName}`);
  // Handle async data processing...
}
export default function UserForm() {
  const [name, formAction] = useActionState(updateName, null);

  return (
    <form action={formAction}>
      <h2>{itemTitle}</h2>
      <input type="text" name="userName" />
      <button type="submit">Update Name</button>
    </form>
  );
}

I pass the updateName “action” and default null value to the useActionState.

When you click the submit button, useActionState will call the updateName action and provide the previous state and all form field values.

The new `<form>` context is convenient but introduces more complexity and increases the learning curve for React ( + 1 thing to the list of things you must remember).

Some developers prefer their custom hooks for simplicity, but it’s hard to ignore the efficiency of native-like hooks.

The new form actions are compatible with both server and client components.

Here is a useActionState example of handling form data on the server side while extracting and displaying it on the client-only side

updateNameServer is a function that React will run on the server side — I explicitly specify it with the `use server` directive.

I import and call this serve function inside the UserFormServer client-side component and React will show an alert with the new name every time you hit an “Update Name” button.

With useActionState, you can implement dual validation patterns — client and server sides.

Client-side validation enhances UX by providing immediate feedback, making the form-filling process more intuitive and less error-prone.

Server-side validation, on the other hand, is crucial for security and ensuring data integrity. It acts as a final gatekeeper before data enters your system.

useFormStatus Hook — Optimize UX With Real-Time Form Feedback for Asset Loading

You can access the status of the last form submission with useFormStatus hook. 

import { useFormStatus } from 'react-dom';

const { pending, data, method, action } = useFormStatus();

useFormStatus returns an object with real-time info about the last form submission, such as whether the submission is pending, the form data, the HTTP request method, and the action itself.

useFormStatus hook is particularly useful when you need access to `<form>` info without drilling down the props. You can show a fallback UI with the pending state and maintain that “pending” state all the way through the data is there.

import { useFormStatus } from 'react-dom';

function LoginButton() {
  const { pending } = useFormStatus();
  return <button type="submit" disabled={pending}>Login</button>;
}

The LoginButton component uses useFormStatus to check if the form submission is pending. If it is, React disables the `<button>` to prevent multiple submissions and provide immediate feedback.

Here’s a simple useFormStatus example where the Submit button disables itself and shows a “Submitting…” while the form submission is in progress:

The submitForm is an asynchronous function that simulates data processing with a setTimeout to create a delay of 1 second, mimicking an async operation.

I call a useFormStatus to obtain the pending state and to check if the form submission is pending. The button’s disabled attribute is set based on the pending state, disabling the button during the submission process.

New React Actions: Making React Component Render Cleaner

With React hooks, everything changes. Your functions become smaller, cleaner, and more intuitive. Instead of bouncing between class methods, your logic streamlines seamlessly through a single function, making sharing functionality between components easier.

Every time we add new features to React — well, to tell the truth — every addition to the library causes us to worry about the bundle size. However, these latest React 19 additions will not fatten your application as much as third-party libraries might, which these new additions will replace (hopefully).

There is a trend of moving away from third-party form management libraries to native React solutions, thereby reducing additional dependencies and working with built-in tools.

The first time I found the React-Hook-Form library, it felt like uncovering a hidden gem.

But as the React grows, so grow the tools and hooks!

The same with Vue — you generally have a solution to a problem, not hundreds of options, all doing the same thing slightly differently. React, however, feels too much like every developer has their version of the wheel.

There is an ongoing discussion about whether React should become a framework instead of a library.

Most argue that React’s strength is its low-level way. Nothing forces devs into a single way of doing things, and they appreciate this flexibility to pick the best tools they need.

Having multiple options feels overwhelming, but they foster competition and innovation. Different tools speak to other use cases — there’s always something that meets your requirements.

Conclusion: JavaScript and React 19 New Features Improved Error Handling and User Experience

Integrate new in React 19 useOptimistic, useActionState, and useFormStatus hooks into your app to provide loading state feedback, manage server interactions, and simplify your codebase!

Experiment with these hooks today, and share your thoughts!!

Share the Post:
Join Our Newsletter