Skip to content
Next.js7 min read

Server Actions in Next.js: Forms and Mutations

Server Actions let you mutate data without writing API routes or client-side fetch calls. Here is how to use them well — validation, errors, revalidation and security included.

For years the standard Next.js mutation flow was: build an API route, write a fetch call, manage loading and error state by hand, then refetch. Server Actions collapse that into a single function that runs on the server and can be passed straight to a form. Less plumbing, progressive enhancement for free, and type safety across the boundary. They deserve to be your default for mutations — with a few disciplines attached.

The shape of a Server Action

A Server Action is an async function marked with the 'use server' directive, defined in a server file and imported wherever you need it. When a form invokes it, Next.js serialises the submission, runs the function on the server, and returns the result — no route handler, no endpoint naming debates.

ts
'use server';

import { revalidatePath } from 'next/cache';

export async function createNote(formData: FormData) {
  const title = String(formData.get('title') ?? '').trim();
  if (!title) return { error: 'Title is required' };
  await db.note.create({ data: { title } });
  revalidatePath('/notes');
  return { error: null };
}

Forms, pending state and useActionState

Bound to a form's action attribute, a Server Action works before JavaScript loads — the browser simply posts the form. Once hydrated, React upgrades it to a client-side transition. For feedback, useActionState gives you the returned state and a pending flag without any manual bookkeeping.

tsx
'use client';

import { useActionState } from 'react';
import { createNote } from './actions';

export function NoteForm() {
  const [state, action, pending] = useActionState(createNote, { error: null });
  return (
    <form action={action}>
      <input name="title" aria-invalid={!!state.error} />
      {state.error && <p role="alert">{state.error}</p>}
      <button disabled={pending}>{pending ? 'Saving…' : 'Save'}</button>
    </form>
  );
}

Because the action is just a function reference, one action can serve several forms, and buttons outside a form can invoke it through the formAction prop. For interactions where waiting feels wrong — toggles, likes, reordering — pair the action with useOptimistic so the interface updates immediately and reconciles when the server confirms.

Validate on the server, every time

A Server Action is a public endpoint. Anyone can invoke it with arbitrary arguments regardless of what your form allows, so client-side validation is a courtesy, not a defence. Parse the incoming FormData with a schema library such as Zod, return structured field errors on failure, and never trust IDs from the payload to imply permission — check that the current user is allowed to touch the record they name. The same schema can be reused on the client for instant feedback without duplicating the rules.

A Server Action is a public endpoint wearing a convenient disguise — secure it like the endpoint it is.

Revalidate after you mutate

A mutation that does not invalidate the relevant caches produces the classic bug: the write succeeds but the page still shows stale data. Call revalidatePath or revalidateTag inside the action, after the write. For redirect-after-create flows, call redirect from the action too — it integrates with the router and the pending state correctly.

Habits that keep actions safe

  • Authenticate inside every action; do not rely on the page being behind auth
  • Keep actions in dedicated files rather than inline, so the server boundary is obvious
  • Return serialisable state — plain objects, not class instances or errors
  • Reach for useOptimistic only where the interaction genuinely benefits, such as likes or toggles

Know when to reach for something else, too. Server Actions run within the request cycle, so genuinely long-running work — imports, bulk emails, media processing — belongs in a queue, with the action doing nothing more than enqueueing the job and returning promptly.

Server Actions reward teams that treat them as what they are: RPC endpoints with excellent ergonomics. Enjoy the ergonomics, keep the endpoint discipline.

Start your project

Have an idea? Let's ship it together.

Tell us what you're building — we'll reply within one business day with an honest take and a clear next step.