Master Next.js Server Actions with this comprehensive guide. Learn how to handle form submissions, mutations, revalidation, and error handling in the App Router without creating API routes.
Server Actions are one of the most powerful features in Next.js 14+. They allow you to run server-side code directly from your components without creating separate API endpoints. Let's dive deep into how to use them effectively.
Server Actions are asynchronous functions that execute on the server. They can be defined in Server Components or imported into Client Components, providing a seamless way to handle:
Here's the simplest form of a Server Action:
// app/actions/user.actions.ts
"use server";
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
export async function createUser(formData: FormData) {
const name = formData.get("name") as string;
const email = formData.get("email") as string;
await prisma.user.create({
data: { name, email },
});
revalidatePath("/users");
}
The most common use case is form handling:
// app/users/new/page.tsx
import { createUser } from "@/actions/user.actions";
export default function NewUserPage() {
return (
<form action={createUser}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Create User</button>
</form>
);
}
Always validate your inputs on the server:
"use server";
import { z } from "zod";
const userSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email address"),
});
export async function createUser(formData: FormData) {
const rawData = {
name: formData.get("name"),
email: formData.get("email"),
};
const validated = userSchema.safeParse(rawData);
if (!validated.success) {
return { error: validated.error.flatten().fieldErrors };
}
// Proceed with database operation
await prisma.user.create({ data: validated.data });
return { success: true };
}
React 19 introduces useActionState for handling action states:
"use client";
import { useActionState } from "react";
import { createUser } from "@/actions/user.actions";
export function UserForm() {
const [state, formAction, isPending] = useActionState(createUser, null);
return (
<form action={formAction}>
<input name="name" placeholder="Name" disabled={isPending} />
<input name="email" placeholder="Email" disabled={isPending} />
{state?.error && (
<p className="text-red-500">{state.error}</p>
)}
<button type="submit" disabled={isPending}>
{isPending ? "Creating..." : "Create User"}
</button>
</form>
);
}
For instant feedback, use optimistic updates:
"use client";
import { useOptimistic } from "react";
import { addTodo } from "@/actions/todo.actions";
export function TodoList({ todos }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);
async function handleSubmit(formData: FormData) {
const title = formData.get("title") as string;
addOptimisticTodo({ id: Date.now(), title });
await addTodo(formData);
}
return (
<>
<form action={handleSubmit}>
<input name="title" />
<button>Add</button>
</form>
<ul>
{optimisticTodos.map((todo) => (
<li key={todo.id} className={todo.pending ? "opacity-50" : ""}>
{todo.title}
</li>
))}
</ul>
</>
);
}
Establish a consistent error handling pattern:
"use server";
type ActionResponse<T = void> = {
success: boolean;
data?: T;
error?: string;
};
export async function safeAction<T>(
action: () => Promise<T>
): Promise<ActionResponse<T>> {
try {
const data = await action();
return { success: true, data };
} catch (error) {
console.error("Action failed:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
Server Actions simplify full-stack development in Next.js by eliminating the need for separate API routes for common operations. They integrate seamlessly with React's form handling and provide excellent TypeScript support.
Start using Server Actions in your next project and experience the improved developer experience they bring!
Get the latest articles, tutorials, and updates delivered straight to your inbox. No spam, unsubscribe at any time.