Actions and forms

Show pending and result states

Use React form hooks to prevent duplicate work and render server validation messages without rebuilding the submission flow by hand.

8 minute lesson

~~~

A form should explain that work is in progress and show expected server results near the relevant controls.

useFormStatus reads the status of a parent form and is useful in a nested submit button. useActionState keeps the serializable result returned by an action. These hooks require Client Components, but the action itself remains server code. Keep native labels, names, and constraints so the form stays understandable before enhancement.

The status hook must run in a component nested inside the form it observes. It does not describe arbitrary requests elsewhere on the page. Use the pending state to prevent confusing repeat interaction and to announce progress, while keeping navigation and recovery actions available.

Connect field messages to inputs with stable IDs and aria-describedby, and set aria-invalid when a field failed. Keep an unexpected server failure visible until the user retries; a disappearing toast is poor evidence that data was not saved. Success should move focus only when the next task genuinely requires it.

'use client'
import { useFormStatus } from 'react-dom'

export function SubmitButton() {
  const { pending } = useFormStatus()
  return <button disabled={pending}>{pending ? "Saving…" : "Save"}</button>
}

Add a delayed action, disable only its submit button, and render an associated title error plus a persistent general failure. Submit twice quickly and remember that the server still needs idempotency even when the UI appears to block the second click.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →