Server Actions are async functions that run on the server and can be called directly from your React components. They replaced the old pattern of wiring up API routes for every mutation. Instead of writing a POST /api/todos route handler, creating a fetch wrapper, and managing loading states manually, you define a function with 'use server' and pass it to a <form action>. The framework handles the network request, serialization, and progressive enhancement for you.
The mental model: a Server Action is an RPC endpoint that Next.js generates automatically. When a user submits a form or clicks a button, the framework serializes the arguments, sends a POST request to the server, executes your function, and streams back the result. Because actions integrate with React's transition system, the UI stays responsive during the round trip. And because they work with the native <form> element, forms function even before JavaScript loads.
Master next.js server actions
Take the Next.js Full-Stack course with hands-on lessons and challenges.
This is the pattern you'll use for most forms. The action receives prevState (for useActionState) and FormData. Zod validates the input on the server, and the structured error object flows back to the client for display. revalidatePath ensures the project list shows the new entry.
useActionState wires the action to the form and provides three values: the last return value from the action (state), a wrapped action to pass to the form, and a pending boolean. The component re-renders with validation errors when the action returns them, and the button disables during submission.
Putting 'use server' at the top of the file marks every exported function as a Server Action. This is the pattern for actions shared across multiple components. Each action checks authentication and ownership before mutating — never trust that the caller is who you expect.
useOptimistic updates the UI immediately while the server action runs in the background. If the action fails, React automatically rolls back to the original state. This gives instant feedback for toggle-style interactions where waiting for the server round trip feels sluggish.
When you need to pass data that isn't in the form (like an entity ID), use Function.prototype.bind. The bound argument arrives as the first parameter, before FormData. This is cleaner than hidden inputs because the value doesn't leak into the HTML.
revalidatePath invalidates a specific URL. revalidateTag invalidates every cached fetch that was tagged with that string, regardless of which page it appears on. Use both when a mutation affects multiple views. Note that redirect must come last because it throws a special exception that stops execution.
Defining a Server Action inside a Client Component — you'll get a build error because `'use server'` cannot be used inline in `'use client'` files
Create a separate file (e.g., `actions.ts`) with `'use server'` at the top, then import the action into your Client Component. Only Server Components can define inline Server Actions.
Trusting that form data is safe because it came from your own UI — a Server Action is a public HTTP endpoint and anyone can call it with arbitrary data
Always validate input with a library like Zod, and always check authentication and authorization inside the action. Treat every Server Action like a public API endpoint.
Using `onSubmit` instead of `action` on the form — this bypasses progressive enhancement and requires manual fetch handling
Pass the Server Action to `<form action={myAction}>`. React extends the native action attribute to invoke the server function. The form will work even before JavaScript hydrates.
Calling `redirect()` before `revalidatePath()` — redirect throws a special exception internally, so any code after it never executes
Always call `revalidatePath()` or `revalidateTag()` before `redirect()`. The redirect must be the last statement in your action because nothing after it will run.
Server Actions are async server-side functions invoked directly from React components via `'use server'`. They replace the boilerplate of creating API routes for mutations. Pass them to `<form action>` for progressive enhancement, validate inputs with Zod, check auth inside every action, and call `revalidatePath`/`revalidateTag` to keep cached data fresh. Use `useActionState` for form validation errors and pending states, and `useOptimistic` when you need instant UI feedback. Reserve Route Handlers for public APIs, webhooks, and streaming responses.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.