React Server Components (RSC) run only on the server. They can read a database, call a private API or access the file system , and they send the rendered result to the browser with no component JavaScript attached. Next.js App Router uses them by default, which makes them part of everyday work for many teams.
Server and client components
A component is a server component unless its file starts with the "use client" directive. That directive marks a boundary: the file and everything it imports run in the browser too.
- Server components fetch data, read secrets and render static or mostly static UI. Their code never reaches the browser.
- Client components handle state, effects, event handlers and browser APIs such as
localStorage. They are still server-rendered on first load, then hydrated.
Fetching data
// app/products/page.tsx (server component)
import { db } from "@/lib/db";
export default async function Products() {
const products = await db.product.findMany({ take: 20 });
return (
<ul>
{products.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
No API route, no useEffect, no loading flag. The database client and its credentials stay on the server. This removes the extra network hop that a client-side fetch needs.
Smaller bundles
If a server component imports a large Markdown parser or date library, that library does not ship to the browser. Only client components contribute to the JavaScript bundle. This often cuts bundle size on content-heavy pages and improves Interaction to Next Paint on slower phones.
Compose them together
A server component can render a client component and pass it props. A client component cannot import a server component, but it can receive one as children:
// Server
<Modal>
<ProductDetails id={id} /> {/* server component rendered inside client Modal */}
</Modal>
Props that cross the boundary must be serializable: strings, numbers, plain objects, arrays, dates and a few others. You cannot pass functions or class instances from a server component to a client component, except for Server Actions.
Push the boundary down
Put "use client" on the smallest component that needs interactivity. A product page needs a client component for the "Add to cart" button and the quantity picker. The title, gallery layout, description and reviews can stay on the server. Marking an entire page as a client component throws away the benefits.
Streaming with Suspense
Wrap slow parts in Suspense with a fallback. The server sends the shell and streams each section as its data resolves.
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={id} />
</Suspense>
Users see the page structure at once, and a slow reviews service does not block the rest.
Mutations with Server Actions
Server Actions are async functions marked "use server" that a form or a client component can call. They replace many hand-written API routes for form submissions:
async function subscribe(formData: FormData) {
"use server";
await saveEmail(String(formData.get("email")));
}
Treat every Server Action as a public endpoint. Validate input, check authentication and authorization inside it, and apply rate limits.
Caching and revalidation
Caching defaults have changed across Next.js versions, so read the documentation for the version you use. Be explicit: set revalidate times for pages that tolerate staleness, tag fetches and call revalidateTag after a mutation, and mark pages that depend on cookies or headers as dynamic.
Common mistakes
- Using
useStateoronClickin a server component. Move that part into a client component. - Importing a server-only module, such as a database client, into a client component. Add
import "server-only"to protect those files. - Fetching sequentially in nested components. Start independent fetches in parallel with
Promise.all. - Putting a context provider at the root without extracting it into its own client file, which turns the whole tree into client code.
- Passing secrets as props to client components, which exposes them in the page payload.
Should you adopt them?
Server Components suit content sites, dashboards, e-commerce catalogs and any app where first load speed and search visibility matter. interactive tools, such as design editors, spend most of their code in client components anyway. Migrate route by route; the App Router and the older Pages Router can live in one project.
Common questions
Do Server Components replace API routes?
They replace many of the routes that existed only to feed your own pages with data. Keep route handlers for webhooks, public APIs, mobile app backends and anything another system calls.
Can I use context, hooks and state libraries?
Not inside server components. Hooks, context and browser APIs belong in client components. Wrap providers in a small client file and render it from your server layout, with the server-rendered pages passed in as children.
How do I debug the boundary errors?
Error messages like "You're importing a component that needs useState" point to a server file importing client-only code. Add "use client" to the smallest file that needs it, and use the server-only and client-only packages to turn accidental imports into clear build errors.
We build with Next.js and the App Router, and migrate existing React apps in stages. Ask about a migration plan or see our Next.js projects.

