Next.js Server Actions: A Practical Guide
Next.js Server Actions: A Practical Guide
Server Actions let you run server-side code directly from React components without building separate API routes. They simplify data mutations, form handling, and server-side logic in Next.js applications.
What Are Server Actions?
A Server Action is an async function marked with "use server" that runs exclusively on the server. You can call it from client components like a regular function, but the execution happens server-side.
"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const body = formData.get("body") as string;
await db.post.create({ data: { title, body } });
revalidatePath("/posts");
}
Using Server Actions in Forms
The simplest pattern is passing a Server Action to a form's action prop. This works with progressive enhancement — the form submits even without JavaScript:
import { createPost } from "./actions";
export default function NewPostForm() {
return (
);
}
Adding Loading States with useActionState
Use the useActionState hook (React 19+) to track pending state and show feedback:
"use client";
import { useActionState } from "react";
import { createPost } from "./actions";
export default function NewPostForm() {
const [state, action, isPending] = useActionState(createPost, null);
return (
);
}
Validation and Error Handling
Always validate inputs on the server. Never trust client-side validation alone:
"use server";
import { z } from "zod";
const PostSchema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(10).max(10000),
});
export async function createPost(prevState: any, formData: FormData) {
const parsed = PostSchema.safeParse({
title: formData.get("title"),
body: formData.get("body"),
});
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
await db.post.create({ data: parsed.data });
revalidatePath("/posts");
return { success: true };
}
When to Use Server Actions vs API Routes
Use Server Actions for form submissions, simple mutations, and operations tightly coupled to your UI components. Use API Routes when you need a public API, webhook endpoints, or when third-party services need to call your backend.Best Practices
revalidatePath or revalidateTag to refresh cached data after mutations.Conclusion
Server Actions eliminate boilerplate by removing the need for API routes in many cases. They provide a clean, type-safe way to handle mutations while supporting progressive enhancement. Start with forms, add validation, and reach for API routes only when you need a standalone endpoint.