devkasun_logo DevKasun

React 19 Form Actions


React v19 comes with great features that are genuinely useful. Instead of discussing all features at once, I’ll cover them one by one so they can be easily applied to our applications. I’ll keep the explanations concise and easy to read.

What is a Form Action?

Form Actions are asynchronous functions that can be directly passed to a form’s action prop, enabling seamless form handling without manual state management.

They automatically manage form submissions and can run on both client and server sides using the “use server” directive.

Think of Form Actions as a smart helper that makes form handling on websites super easy! 😉

Key Features:

  • Automatic state management
  • Built-in pending states during form submission
  • Optimistic updates through the useOptimistic hook
  • Error handling with Error Boundaries
  • Automatic form reset after successful submission

When it comes to Form Actions, we can’t ignore the new useActionState hook.

What is useActionState?

The useActionState is a React Hook designed to manage form actions and state updates in React components.

const [state, formAction, isPending] = useActionState(
  actionFunction,
  initialState
);

The hook returns three elements:

  • state: The current state value
  • formAction: A new action function for forms
  • isPending: A boolean indicating if the action is in progress

Think of useActionState as a smart traffic light for your forms - it helps you track your form’s status at any moment, whether it’s ready to submit, processing information, or encountering errors.

Here’s a practical example:

const [state, submit, isBusy] = useActionState

  async (previous, formData) => {

    // Like sending a letter and waiting for a reply

    const message = await sendToServer(formData.get("message"));

    return message;

  },

  "Ready to send!" // Starting message

);

Here’s another practical example for a login form. Just ignore my Tailwind CSS classes. 😅

import { useActionState } from "react";

function FormActionExample() {
  const [state, formActionFunc] = useActionState(
    async (prevState, formData) => {
      const name = formData.get("email");
      const password = formData.get("password");

      const attempts = (prevState?.attempts || 0) + 1;

      console.log(name);
      console.log(password);

      return { message: "Login Success", attempts: attempts };
    }
  );

  return (
    <div className="w-full flex flex-col items-center gap-4 text-center">
      <h3 className="text-xl"> --- Client Side --- </h3>
      <form
        action={formActionFunc}
        className="flex flex-col gap-4 max-w-72 w-full"
      >
        <input
          type="email"
          name="email"
          placeholder="User Email"
          className="border-2 border-solid border-black rounded-md p-2"
        />
        <input
          type="password"
          name="password"
          placeholder="User Password"
          className="border-2 border-solid border-black rounded-md p-2"
        />
        <button
          type="submit"
          className="border-2 border-solid border-black rounded-md p-2 bg-black text-white"
        >
          Login
        </button>
      </form>

      {state?.message && <p>{state.message}</p>}
      {state?.attempts && <p>Login attempts: {state.attempts}</p>}
    </div>
  );
}

export default FormActionExample;

If you are interested in Form Actions and Vite setup for React 19, checkout my Github repository here.