DEV Community

Cover image for Next.js Server Actions: Complete Guide with Examples for 2026
Md. Maruf Rahman
Md. Maruf Rahman

Posted on • Edited on • Originally published at practicaldev.online

Next.js Server Actions: Complete Guide with Examples for 2026

Server Actions are one of those features that completely changed how I think about form handling in Next.js. Instead of creating API routes and managing fetch calls, you can write server-side functions that work seamlessly with your forms. The best part? They work even when JavaScript is disabled—progressive enhancement built right in.

When I first started using Next.js, handling forms meant creating API routes, writing fetch calls, managing loading states, and dealing with error handling. It worked, but it felt like a lot of boilerplate for something that should be simple. Then Server Actions came along, and everything clicked.

What Are Server Actions?

Server Actions are async functions that run on the server. They're marked with the 'use server' directive and can be called directly from client or server components. When used with forms, they provide progressive enhancement—your forms work even if JavaScript fails to load.

Here's the simplest example:

"use server";

import { createPost } from "./lib/posts-store";

export async function createPostAction(title) {
  const post = createPost(title);
  return post;
}
Enter fullscreen mode Exit fullscreen mode

Basic Form with Server Action

The simplest way to use a Server Action is with a form:

"use server";

import { revalidatePath } from "next/cache";
import { createPost } from "./lib/posts-store";

export async function createPostAction(prevState, formData) {
  const title = formData.get("title");

  if (!title || title.trim().length === 0) {
    return {
      message: "Title is required",
      fieldErrors: { title: "Title cannot be empty" },
    };
  }

  const post = createPost(title);
  revalidatePath("/");

  return {
    message: "Post created successfully",
    fieldErrors: { title: "" },
  };
}
Enter fullscreen mode Exit fullscreen mode

Now, here's how you use it in a form:

"use client";

import { useActionState } from "react";
import { createPostAction } from "./posts-actions";

const initialState = {
  message: "",
  fieldErrors: { title: "" },
};

export default function CreatePostForm() {
  const [state, formAction, pending] = useActionState(
    createPostAction,
    initialState
  );

  return (
    <form action={formAction}>
      <input name="title" required disabled={pending} />
      {state.fieldErrors.title && (
        <p role="alert" className="text-red-500">
          {state.fieldErrors.title}
        </p>
      )}
      <button type="submit" disabled={pending}>
        {pending ? "Adding..." : "Add post"}
      </button>
      <p aria-live="polite">{state.message}</p>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Form Validation with Zod

Validation is crucial for any form:

"use server";

import { revalidatePath } from "next/cache";
import { z } from "zod";
import { createPost } from "./lib/posts-store";

const createPostSchema = z.object({
  title: z
    .string()
    .trim()
    .min(1, "Title is required")
    .max(80, "Title is too long"),
});

export async function createPostAction(prevState, formData) {
  const raw = Object.fromEntries(formData);
  const result = createPostSchema.safeParse(raw);

  if (!result.success) {
    const fieldErrors = result.error.flatten().fieldErrors;
    return {
      message: "Please fix the errors and try again.",
      fieldErrors: { title: fieldErrors.title?.[0] || "" },
    };
  }

  const post = createPost(result.data.title);
  revalidatePath("/");

  return {
    message: "Post added.",
    fieldErrors: { title: "" },
  };
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use Server Actions for form handling
  2. Validate with Zod on the server
  3. Use useActionState for form state
  4. Revalidate paths after mutations
  5. Handle errors gracefully
  6. Provide loading states

📖 Read the Complete Guide

This is just a brief overview! The complete guide on my blog includes:

  • Advanced Patterns - Complex form handling, file uploads
  • Cookies & Headers - Working with cookies and headers
  • Revalidation - Cache invalidation strategies
  • Error Handling - Complete error management
  • Progressive Enhancement - Forms without JavaScript
  • Real-world examples from production applications

👉 Read the full article with all code examples here


What's your experience with Next.js Server Actions? Share your tips in the comments! 🚀

For more Next.js guides, check out my blog covering Caching, Rendering, and more.

Top comments (0)