The Journal

React & Performance3 min read

React 19 Server Actions: The Complete Guide to Full-Stack React Components

Master React 19 Server Actions to build full-stack components that handle data mutations, form submissions, and server-side logic without writing API routes.

You built an API route, a fetch wrapper, a loading flag, and an error toast — all to save one form. React 19 deletes most of that. A Server Action is a function that runs on the server and wires straight into your form: no route, no fetch, no client glue.

The action

Mark a function with use server and it becomes callable from the client without an endpoint. Pass it to a form’s action prop and the browser posts to it directly. You read fields off FormData, do the work, and revalidate.

app/actions.ts
"use server";

export async function createTodo(formData: FormData) {
  const title = String(formData.get("title") ?? "").trim();
  if (!title) return { error: "Title is required" };
  await db.todo.create({ data: { title } });
  revalidatePath("/todos");
}

That one file replaces a route handler, a client fetch, and the type you kept in sync between them. Delete all three.

State and pending

For feedback, wrap the action in useActionState. It hands you the latest return value, a wrapped action for the form, and an isPending flag — no useState, no manual try/catch.

Return an object with field-level errors and render them next to each input. One function does the mutation and the messaging.

For a plain submit button, useFormStatus is lighter still. It reads the parent form’s pending state, so you can disable the button and swap its label while the action runs.

Progressive enhancement

Because the action sits on the form’s action prop, the form works before any JavaScript loads. Submit early, on a slow phone, with JS disabled — the browser posts, the server runs, the page updates.

Once React hydrates, that same submit becomes an in-place update with no full reload. You wrote it once and got both behaviors free.

This matters more than it sounds. The slowest moment for any visitor is that first load, before the JavaScript is ready — and that’s exactly when a plain action form still works.

The rule

Client validation is a courtesy. The server is the wall. Validate every field there with something like Zod, and confirm auth before you touch the database.

Keep each action small — one job per function. Composition beats a 200-line mega-action you can’t test in isolation.

So: one function, use server, wired to action. Validate on the server, return errors as data, let the form degrade gracefully. That’s the whole pattern.

Frequently asked questions

What is a Server Action in React 19?
A Server Action is a function marked with the use server directive that runs on the server and is callable from the client without an API route. You pass it to a form's action prop, read fields off FormData, perform the mutation, and revalidate the affected path.
How do I show loading and error states with React 19 Server Actions?
Wrap the action in useActionState to get the latest return value, a wrapped action for the form, and an isPending flag, with no useState or manual try/catch. For a plain submit button, useFormStatus reads the parent form's pending state so you can disable it and swap its label.
Are React 19 Server Actions secure by default?
No. Treat every Server Action as a public endpoint, because anyone can post to it. useActionState handles form plumbing, not authorization, so check the session and validate every field on the server, using something like Zod, before touching the database.