Server Components & Client Components
Part of the Full Stack Developer Roadmap
In short
In Next.js, most components can run only on the server, read straight from the database and send zero JavaScript to the browser. The skill is spotting the few that must be client components.
Next.js's default of rendering components on the server (no client JS shipped) unless explicitly marked `'use client'` — blurring the line between 'frontend' and 'backend' code even further.

Think of it like this
Thali vs the live dosa counter
At a wedding, most dishes are cooked in the back kitchen and arrive ready on your plate. The dosa counter is different: it cooks in front of you and changes as you ask, 'extra crispy, less ghee'. Server components are the ready dishes. Client components are the live counter.
Watch it happen
Which parts ship JavaScript?
1/5page.tsx is a server component by default. It can be async and query the database directly.
The big ideas
What you need to know
Server is the default
In the App Router every component is a server component unless its file starts with 'use client'.
Fetch where you render
Server components can be async and read the database or call secret APIs. No useEffect and no extra API route.
Client for interaction
Anything with useState, useEffect, onClick or browser APIs like localStorage must be a client component.
Push 'use client' down
Mark the smallest leaf that needs interactivity. Marking the whole page sends everything it imports to the browser.
See it in code
Walk through the code
1// app/products/page.tsx: a server component (the default)2import { db } from "@/lib/db";3import { AddToCart } from "./add-to-cart";4export default async function ProductsPage() {5 const products = await db.product.findMany({ take: 20 });6 return <ul>{products.map((p) => <li key={p.id}>{p.name} <AddToCart productId={p.id} /></li>)}</ul>;7}8 9// app/products/add-to-cart.tsx10"use client";11import { useState } from "react";12export function AddToCart({ productId }: { productId: number }) {13 const [added, setAdded] = useState(false);14 return <button onClick={() => setAdded(true)} disabled={added}>{added ? "Added" : "Add to cart"}</button>;15}1/4The page is async and queries the database right inside the component. That code stays on the server.
Two files, two worlds. A real AddToCart would also send productId to a server action; this one just shows where state lives.
Save yourself the pain
Common mistakes
Oops
Adding 'use client' to page.tsx because one button needs onClick.
Do this instead
Move the button into its own client component and keep the page on the server.
Oops
Importing your database client into a client component.
Do this instead
Keep database and secret code in server files. The server-only package can make such an import fail the build.
Your mission
Split a page into server and client
Quick check
Which of these must be a client component?
Go deeper
- 1Fetch data directly inside a Server Component with `async`/`await`, no API call needed.
- 2Add `'use client'` to a component that needs `useState` or an event handler.
- 3Learn to keep the client-component boundary small for better performance.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.