Server Actions are async functions marked with 'use server' that run exclusively on the server — they replace API routes for mutations (form submissions, data updates), support progressive enhancement (forms work without JavaScript), and integrate with React's useActionState and useOptimistic for pending/optimistic UI.
Async functions that run exclusively on the server — code never ships to the client. Called from forms via action prop or from event handlers.
Forms using Server Actions work with JavaScript disabled (standard POST submit). With JS enabled, submission is seamless without full page reload.
useActionState provides pending state and form results. useOptimistic shows expected results immediately while the server processes.
Server-only code, but always validate inputs (they're POST endpoints). Revalidate cached data after mutations with revalidatePath/revalidateTag.
Server Actions are the App Router's mechanism for handling data mutations — form submissions, database writes, and any operation that changes data. They replace the pattern of creating API routes and calling them with fetch.
A Server Action is an async function marked with 'use server' that runs exclusively on the server:
// In a Server Component or separate file
async function createPost(formData: FormData) {
'use server';
const title = formData.get('title') as string;
const content = formData.get('content') as string;
await db.posts.create({ data: { title, content } });
revalidatePath('/posts');
}When called from the client, Next.js automatically creates a POST endpoint — the function code stays on the server. The client sends form data, the server executes the function, and the response updates the page.
Server Actions work directly with HTML forms via the action prop:
async function ContactPage() {
async function submitForm(formData: FormData) {
'use server';
await sendEmail(formData.get('email'), formData.get('message'));
redirect('/thank-you');
}
return (
<form action={submitForm}>
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
);
}Progressive Enhancement: This form works even with JavaScript disabled — the browser submits the form as a standard POST request. With JavaScript enabled, Next.js intercepts the submission for a smoother experience (no full page reload).
For reusable actions, create a file with 'use server' at the top:
// app/actions.ts
'use server';
export async function createUser(formData: FormData) {
// Runs on server — safe to use secrets, database connections
const user = await db.users.create(...);
revalidatePath('/users');
return user;
}These can be imported by both Server and Client Components.
useActionState (React 19) provides form state and pending status:
'use client';
import { useActionState } from 'react';
import { createUser } from './actions';
function SignupForm() {
const [state, formAction, isPending] = useActionState(createUser, null);
return (
<form action={formAction}>
<input name="email" />
<button disabled={isPending}>
{isPending ? 'Creating...' : 'Sign Up'}
</button>
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}useOptimistic shows the expected result immediately while the server processes:
'use client';
import { useOptimistic } from 'react';
function TodoList({ todos }) {
const [optimisticTodos, addOptimistic] = useOptimistic(todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);
async function addTodo(formData: FormData) {
addOptimistic({ title: formData.get('title') });
await createTodo(formData);
}
return <form action={addTodo}>...</form>;
}Server Actions are secure by default:
After a mutation, update the UI by revalidating cached data:
revalidatePath('/posts') — revalidate a specific routerevalidateTag('posts') — revalidate all fetches tagged 'posts'redirect('/success') — redirect to a new pageServer Actions are async functions with 'use server' that handle mutations from forms and event handlers. They replace API routes for data writes. Progressive enhancement means forms work without JavaScript. useActionState provides pending states. useOptimistic shows immediate UI feedback. Always validate inputs — Server Actions are POST endpoints that can receive arbitrary data. Revalidate cached data after mutations with revalidatePath/revalidateTag.
Fun Fact
Server Actions were inspired by PHP — the original 'server action' pattern where form submissions went directly to server-side code. React and Next.js essentially reinvented the PHP form handling model with modern ergonomics: type safety, optimistic UI, streaming updates, and progressive enhancement. The React team has joked that they've come 'full circle' back to server-side form processing.