Why Server Actions? Answering the Most Common Questions - By Sourav Mishra (@souravvmishra)
Should you use Next.js Server Actions? Understand when they make sense, when to skip them, and how they compare to API routes and React Query.
"What's the point of Server Actions?"
I see this question everywhere - Reddit, Discord, Twitter. Developers who've been building with Next.js for years look at Server Actions and wonder: why should I use this instead of what I already know?
Let me break it down.
The Question Everyone Asks
Here's the typical confusion:
"I understand Server Components for fetching data, but why would I want my POST/PUT/DELETE requests to run on the server? I've been using API routes or tRPC for years. What's the benefit?"
It's a fair question. Let's get into it.
What Server Actions Replace
Before Server Actions, here's how you'd handle a form submission in Next.js:
// 1. Create an API route
// pages/api/create-post.ts
export default async function handler(req, res) {
const { title, content } = req.body;
await db.posts.create({ title, content });
res.json({ success: true });
}
// 2. Call it from your component
"use client";
function CreatePostForm() {
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const res = await fetch("/api/create-post", {
method: "POST",
body: JSON.stringify({
title: formData.get("title"),
content: formData.get("content"),
}),
});
// Handle response, update UI, etc.
};
return <form onSubmit={handleSubmit}>...</form>;
}
That's two files, manual fetch calls, JSON serialization, and no type safety between the client and server.
The Server Action Equivalent
// app/actions.ts
"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
await db.posts.create({ title, content });
revalidatePath("/posts");
}
// app/posts/new/page.tsx
import { createPost } from "@/app/actions";
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" />
<textarea name="content" />
<button type="submit">Create</button>
</form>
);
}
One function. Direct import. Type-safe. No API route boilerplate.
The Real Benefits
1. Progressive Enhancement
Forms using Server Actions work without JavaScript. The form submits via a standard POST request, the server handles it, and the page updates.
This matters for:
- Users on slow connections where JS hasn't loaded yet
- Accessibility tools and screen readers
- SEO crawlers
Traditional client-side forms break completely without JS.
2. Colocation
Your mutation logic lives next to where it's used. No jumping between /api/ routes and components. The mental overhead drops significantly.
// Everything in one place
export default function SettingsPage() {
async function updateProfile(formData: FormData) {
"use server";
// validation, database update, revalidation
}
return <form action={updateProfile}>...</form>;
}
3. Automatic Type Safety
When you import a Server Action, TypeScript knows its signature. No need for zod schemas on both client and server, no type mismatches.
For more on TypeScript patterns that work great with Server Actions, check out my TypeScript Generics guide.
4. Built-in Integration with Caching
Server Actions integrate directly with Next.js caching:
"use server";
import { revalidatePath, revalidateTag } from "next/cache";
export async function createPost(formData: FormData) {
await db.posts.create({ ... });
// The posts page automatically shows the new post
revalidatePath("/posts");
}
No need to manually invalidate React Query caches or trigger refetches.
5. Single Server Roundtrip
When you call a Server Action, Next.js returns both the action result AND the updated React tree in one response. With traditional approaches, you'd:
- POST to API → get response
- Manually trigger a refetch OR update local state
- Re-render components
Search Console section. Server Actions collapse this into one round trip.
When NOT to Use Server Actions
Server Actions aren't always the answer:
1. Complex Authentication/Middleware
If you need fine-grained control over headers, CORS, rate limiting, or middleware, API routes give you more flexibility.
2. External API Integrations
If you're building a webhook endpoint or a public API that non-Next.js clients will call, use API routes.
3. Long-Running Jobs
Server Actions should return quickly. For video processing, report generation, or anything that takes more than a few seconds, trigger a background job and poll for status instead.
4. You Need React Query's Features
React Query provides:
- Automatic background refetching
- Optimistic updates with rollback
- Infinite scroll pagination
- Request deduplication
If you heavily rely on these, React Query still makes sense. You can even combine them:
"use client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { createPost } from "@/app/actions";
function CreatePostButton() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries(["posts"]);
},
});
return <button onClick={() => mutation.mutate(formData)}>Create</button>;
}
Server Actions vs. tRPC
Another common question: "I use tRPC. Should I switch?"
tRPC gives you:
- End-to-end type safety
- Batched requests
- Subscriptions
If you're already invested in tRPC and love it, there's no urgent need to migrate. Server Actions are a simpler alternative for simpler use cases.
The Mental Model Shift
The real value of Server Actions isn't any single feature - it's the mental model change.
Instead of thinking:
"I need to create an API endpoint, then call it from the client, then handle the response, then update the cache..."
You think:
"I need to mutate data. Here's the function that does it."
The boundary between client and server becomes almost invisible. You just write functions and use them.
My Recommendation
-
For new projects: Use Server Actions for mutations by default. Add API routes when you need them.
-
For existing projects: Gradually adopt Server Actions for new features. No need to rewrite what works.
-
For simple CRUD: Server Actions are almost always the right choice.
-
For complex state management: Combine with React Query or your existing state solution.
Ready to implement Server Actions? Check out my complete guide: Next.js Server Actions: Complete Guide with Form Handling & Validation.
If you're also modernizing your CSS, don't miss CSS :has() Selector Guide - another game-changer that reduces JavaScript in your codebase.
Frequently Asked Questions
Q: What are Next.js Server Actions?
Server Actions are async functions that run on the server and can be called directly from React components. They replace API routes for mutations, providing type-safe, colocated server logic with automatic form handling.
Q: Should I use Server Actions or API routes?
Use Server Actions for form submissions, CRUD operations, and simple mutations. Use API routes for webhook endpoints, external API integrations, complex authentication middleware, or when non-Next.js clients need access.
Q: Can I use Server Actions with React Query?
Yes. You can use Server Actions as the mutation function in React Query's useMutation. This gives you Server Actions' simplicity with React Query's caching, optimistic updates, and retry logic.
Q: Do Server Actions work without JavaScript?
Yes. Forms using Server Actions work via standard POST requests even if JavaScript hasn't loaded. This is called progressive enhancement and improves accessibility and reliability.