Next.js forms combine HTML semantics, Server Actions, and optional client validation. The goal: reliable mutations with clear pending and error states.
Pending state with useFormStatus
'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Saving…' : 'Save'}</button>;
}
Client + server validation
- Client: instant feedback (required fields, format)
- Server: authoritative checks (permissions, uniqueness, sanitization)
Controlled inputs in Client Components
Rich widgets (tag pickers, WYSIWYG) stay client-controlled; submit through a Server Action or hidden fields synced before submit.
Self-check
- Why validate on both client and server?
- What does
useFormStatusrequire parent-wise?
Challenge
Form submit sim
- Type a todo title and submit.
- Confirm pending then success states.
- Try empty submit and see validation.
Done when: form shows pending, success, or validation error.
Tip: useFormStatus must sit inside a component that is a child of <form action={...}>—not in the same component that owns the form.