useActionState — Read Action Result on the Client

State, Action, Pending — From One Hook

useActionState — Read Action Result on the Client

React's `useActionState` (formerly `useFormState`) lets a Client Component read what an action returned — perfect for showing validation errors next to the form.

4 min read Level 3/5 #nextjs#forms#react
What you'll learn
  • Wrap an action with `useActionState`
  • Render returned errors and messages
  • Use the pending state for disabled buttons

A bare <form action> calls the action and revalidates — but it has no way to surface “email is invalid” back to the user. useActionState gives the action a return value that the client can render.

The Hook Signature

const [state, formAction, pending] = useActionState(action, initialState).

The action becomes formAction. Its first argument is the previous state; its second is the FormData.

Wiring It Up

// actions.ts
'use server'
import { revalidatePath } from 'next/cache'

type State = { error: string | null }

export async function createNote(prev: State, form: FormData): Promise<State> {
  const title = String(form.get('title') ?? '')
  if (title.length < 3) return { error: 'Title must be at least 3 characters' }
  await db.notes.insert({ title })
  revalidatePath('/notes')
  return { error: null }
}
// NoteForm.tsx
'use client'
import { useActionState } from 'react'
import { createNote } from './actions'

export default function NoteForm() {
  const [state, formAction, pending] = useActionState(createNote, { error: null })
  return (
    <form action={formAction}>
      <input name="title" />
      {state.error && <p className="error">{state.error}</p>}
      <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>
    </form>
  )
}

The error text renders only on a failed submission. Pending state powers the button label.

Why This Beats Plain useState

Pending tracking is handled by React, including during the revalidation phase after a successful action. You do not have to wire up your own loading flags or worry about race conditions between submits.

useFormStatus →