useActionState

Track a Server Action's result and pending state in a Client Component with the useActionState hook.

Syntaxconst [state, formAction, pending] = useActionState(action, initialState);

To show validation messages or a result after a Server Action runs, use React's useActionState hook in a Client Component. It wraps your action, gives you the latest returned state, and a pending flag for disabling the button while it runs.

What it returns

  • state — the value your action returned (e.g. an error message).
  • formAction — pass this to the form's action.
  • pendingtrue while the action is in flight.

Example

Example · typescript
'use client';
import { useActionState } from 'react';
import { subscribe } from './actions';

export default function Form() {
  const [state, formAction, pending] = useActionState(subscribe, null);
  return (
    <form action={formAction}>
      <input name="email" type="email" />
      <button disabled={pending}>
        {pending ? 'Saving…' : 'Subscribe'}
      </button>
      {state?.error && <p>{state.error}</p>}
    </form>
  );
}

When to use it

  • A registration form shows field errors returned from a Server Action using useActionState without a redirect.
  • A subscribe button displays a pending spinner while the Server Action processes and shows a success message on completion.
  • A search form updates its result count in real time as the Server Action re-runs with the latest query.

More examples

Basic useActionState usage

useActionState wires the Server Action to the form, gives access to the returned state, and tracks the pending flag.

Example · ts
'use client';
import { useActionState } from 'react';
import { signUp } from '@/app/actions';

export default function SignUpForm() {
  const [state, action, isPending] = useActionState(signUp, null);
  return (
    <form action={action}>
      <input name="email" />
      {state?.error && <p>{state.error}</p>}
      <button disabled={isPending}>
        {isPending ? 'Signing up...' : 'Sign up'}
      </button>
    </form>
  );
}

Server action returning state

The action receives the previous state as its first argument and returns the new state object for useActionState.

Example · ts
'use server';

export async function signUp(_prev: unknown, formData: FormData) {
  const email = formData.get('email') as string;
  if (!email.includes('@')) {
    return { error: 'Invalid email address' };
  }
  await createUser(email);
  return { success: true };
}

Show success after action

Checking state.success allows the component to swap from the form to a confirmation message after the action completes.

Example · ts
'use client';
import { useActionState } from 'react';
import { subscribeAction } from '@/app/actions';

export default function SubscribeButton() {
  const [state, action, isPending] = useActionState(subscribeAction, null);
  if (state?.success) return <p>Subscribed!</p>;
  return (
    <form action={action}>
      <button disabled={isPending}>Subscribe</button>
    </form>
  );
}

Discussion

  • Be the first to comment on this lesson.