C.W.K.
Stream
Lesson 03 of 08 · published

useActionState

~22 min · useActionState, form state, errors

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Renamed in React 19

useFormState from react-dom is gone. Use useActionState from react. The new hook also returns a third value, isPending — you no longer need useFormStatus for the common case of disabling the submit button.

Shape

const [state, formAction, isPending] = useActionState(action, initialState); — render state, pass formAction to the form, disable on isPending.

Action signature

The action receives (prevState, formData) and returns the next state. Whatever it returns becomes state on the next render. This is your channel for "the action ran, here's what happened": success messages, validation errors, optimistic markers.

Code

useActionState in a sign-up form·tsx
'use client';
import { useActionState } from 'react';

async function signUp(prev: any, formData: FormData) {
  'use server';
  const email = formData.get('email') as string;
  if (!email.includes('@')) return { error: 'Invalid email', success: false };

  await db.user.create({ data: { email } });
  return { error: null, success: true };
}

export default function SignUpForm() {
  const [state, formAction, pending] = useActionState(signUp, {
    error: null,
    success: false,
  });

  return (
    <form action={formAction} className="space-y-2">
      <input name="email" type="email" required className="border px-2 py-1" />
      {state.error && <p className="text-red-500 text-sm">{state.error}</p>}
      {state.success && <p className="text-green-600 text-sm">Welcome!</p>}
      <button disabled={pending} className="rounded bg-blue-600 px-3 py-1 text-white">
        {pending ? 'Signing up&hellip;' : 'Sign up'}
      </button>
    </form>
  );
}

External links

Exercise

Convert a form that currently uses useFormState from react-dom to useActionState from react. Show the diff and confirm pending state now comes from the hook itself.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.