Form Actions and useActionState in React 19

A
Author
19 Sep, 2026 1 min read 219 views

Before React 19, a form needed state for values, errors, loading and the response. useActionState folds most of that into one hook.

The signature

const [state, formAction, isPending] = useActionState(actionFn, initialState);
// actionFn(previousState, formData) => newState

A signup form with validation

import { useActionState } from 'react';

async function signup(prevState, formData) {
  const email = formData.get('email');
  const password = formData.get('password');

  if (!email.includes('@')) return { error: 'Enter a valid email.' };
  if (password.length < 8) return { error: 'Password must be at least 8 characters.', email };

  const res = await fetch('/api/signup', { method: 'POST', body: formData });
  if (!res.ok) return { error: 'That email is already registered.', email };
  return { success: true };
}

export default function SignupForm() {
  const [state, formAction, isPending] = useActionState(signup, {});

  if (state.success) return <p>Check your inbox to confirm your account.</p>;

  return (
    <form action={formAction}>
      <input name="email" type="email" defaultValue={state.email} />
      <input name="password" type="password" />
      {state.error && <p role="alert">{state.error}</p>}
      <button disabled={isPending}>{isPending ? 'Creating account...' : 'Sign up'}</button>
    </form>
  );
}

Pending state in a child: useFormStatus

import { useFormStatus } from 'react-dom';

function SubmitButton({ children }) {
  const { pending } = useFormStatus(); // reads the closest parent <form>
  return <button disabled={pending}>{pending ? 'Saving...' : children}</button>;
}

Things to know

  • Uncontrolled inputs (defaultValue) are the norm with actions; you read values from FormData.
  • After a successful action, React resets the form's uncontrolled fields.
  • In frameworks with Server Functions (such as Next.js), the action can run on the server, and the form works even before JavaScript loads.
  • Always validate on the server too; client checks are only for user experience.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.