Developers conflate Server Components and Server Actions constantly, and it's understandable — both are "server-y" things in React. But they solve fundamentally different problems: one is about how UI renders, the other about how state changes. Once you internalize the distinction the rest of the App Router model clicks into place. This piece is the mental model plus the patterns that show up in real production code.
What changed in 2026
- React 19 stabilized. Server Components, Server Actions, and
useActionState are mature; the early-2024 footguns are mostly fixed.
- Form-state libraries collapsed. With Server Actions +
useActionState, most apps no longer need Formik or React Hook Form for basic forms.
- The "Use Client" boundary is well-understood. Two years in, teams know where to draw it and why.
What Server Components actually do
A Server Component is a React component that runs only on the server. It can:
- Fetch data directly (no API layer needed for the read path).
- Access server-only resources (database, files, secrets).
- Return JSX that gets serialized and streamed to the client.
It cannot:
- Use
useState, useEffect, or any browser-only hook.
- Attach event handlers.
Server Components solve the read problem. Where you used to write a client component plus a useEffect plus a /api/foo route to fetch and display data, you now write one async server component.
What Server Actions actually do
A Server Action is a function marked with 'use server' that you can call directly from a client (typically a form). It can:
- Mutate server state (write to DB, call APIs).
- Revalidate cache (
revalidatePath, revalidateTag).
- Run on the server, never bundled to client JS.
It cannot:
- Render UI (it returns data, not JSX).
- Be called from outside React's data flow.
Server Actions solve the write problem. Where you used to write a client form plus a /api/foo POST route plus manual cache invalidation, you now write one server action.
The pattern: read with RSC, write with action, revalidate
// page.tsx - Server Component (read)
export default async function Page() {
const items = await db.items.findMany();
return <ItemList items={items} />;
}
// actions.ts - Server Action (write)
'use server';
export async function addItem(formData: FormData) {
await db.items.create({ name: formData.get('name') });
revalidatePath('/');
}
// AddItemForm.tsx - Client component for interactivity
'use client';
export function AddItemForm() {
return <form action={addItem}><input name="name" /><button>Add</button></form>;
}
This is the canonical shape. Server Component reads, Server Action writes, revalidation closes the loop.
When to use what
| Need |
Use |
| Display data from DB |
Server Component |
| Form submission |
Server Action |
| Client-side interactivity (modal, dropdown) |
Client Component |
| Real-time updates |
Client Component + WebSocket/SSE |
| External API mutation |
Server Action |
| File upload |
Server Action with FormData |
| Optimistic UI |
Client Component + useOptimistic + Server Action |
Common mistakes
Treating Server Actions as a generic RPC. They're designed for mutations with revalidation. For arbitrary RPC, build a proper API.
Calling Server Actions from non-form contexts without thinking. Yes, you can await myAction() from any client component. But the form-action binding is the ergonomic happy path.
Forgetting to revalidate. A Server Action that mutates without revalidatePath or revalidateTag leaves stale UI.
Mixing client and server in confusing ways. Keep the "use client" boundary explicit and small. Pass server-fetched data down as props.
What about API routes?
API routes (Route Handlers in App Router) still have a place:
- Webhook endpoints.
- Third-party API integrations (Stripe webhook, GitHub app).
- Endpoints consumed by non-React clients (mobile apps, external services).
For internal data flow (React UI talking to your DB), Server Components + Server Actions replace ~80% of what API routes used to do.
FAQ
Can Server Actions return data?
Yes — they can return any serializable value. useActionState makes it easy to handle the response.
Are Server Actions cached?
No, they always execute. Use unstable_cache if you want memoization.
Server Actions in non-Next.js frameworks?
Yes — Remix loaders/actions, TanStack Start, Waku all support analogous patterns now.
Is this only Next.js?
Server Components are a React feature; multiple frameworks implement them. Next.js was first to ship at scale; Remix and TanStack are catching up in 2026.
Where to go next
For related material see Next.js 15 Server Actions guide in 2026, Svelte 5 vs React 19 in 2026, and tRPC vs GraphQL vs REST in 2026.