React 19 has fundamentally changed how forms work in React applications, replacing complex third-party form libraries with a clean native approach powered by Server Actions and the useActionState hook. Muhammad Sufyan of Sufyan Frontend — who handles forms in production Next.js applications — shares the complete 2026 guide to modern React form handling.
The Old Way vs the New Way
Before React 19, handling async form submissions typically required a combination of useState for field values, a loading state, an error state, and a handleSubmit function with manual async logic — often also pulling in a form library like React Hook Form for validation. This boilerplate was repetitive and error-prone.
React 19 replaces this entire pattern with Server Actions and useActionState. The form's action attribute accepts a Server Action directly, and useActionState automatically manages the pending state, error state, and result — giving you a cleaner, more powerful pattern with significantly less code.
useActionState in Practice
// app/contact/actions.ts
"use server";
export async function submitContactForm(prevState: unknown, formData: FormData) {
const name = formData.get("name") as string;
const email = formData.get("email") as string;
if (!name || !email) return { error: "All fields are required." };
await sendEmail({ name, email });
return { success: true };
}
// ContactForm.tsx
"use client";
import { useActionState } from "react";
import { submitContactForm } from "./actions";
export function ContactForm() {
const [state, formAction, isPending] = useActionState(submitContactForm, null);
return (
<form action={formAction}>
<input name="name" placeholder="Your name" required />
<input name="email" type="email" placeholder="Email" required />
{state?.error && <p className="text-red-500">{state.error}</p>}
{state?.success && <p className="text-green-500">Message sent!</p>}
<button type="submit" disabled={isPending}>
{isPending ? "Sending..." : "Send Message"}
</button>
</form>
);
}Validation with Zod and Server Actions
- ▸Define a Zod schema for your form data and validate in the Server Action
- ▸Return validation errors as part of the action state for field-level display
- ▸Use
useFormStatusin submit button components to read parent form pending state - ▸Server-side validation is always required — client-side validation is a UX enhancement only
Conclusion
React 19's approach to forms with Server Actions and useActionState is cleaner and more powerful than any previous form handling pattern. Muhammad Sufyan uses this pattern on production projects including the contact form on his portfolio at https://sufyan-frontend.vercel.app. Make the switch and eliminate the form library boilerplate from your Next.js applications.