useActionState
Track a Server Action's result and pending state in a Client Component with the useActionState hook.
Syntax
const [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'saction.pending—truewhile the action is in flight.
Example
'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.
'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.
'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.
'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