
TypeScript or nothing
One language from the phone to the database. Here's why every project I take on is TypeScript end to end, and why I stopped apologising for it.
Server Components confuse people because the mental model is missing. Here's the one that finally makes the client/server split click, plus the rules that keep it clean.

Forget the tech for a second. Picture a restaurant kitchen.
The kitchen preps everything out of sight. It has the raw ingredients, the walk-in fridge, the recipes nobody at the table should see. By the time a plate reaches you, the mess is gone. You get the finished dish, not the chopping.
That's a React Server Component. It does its work on the server, near the data, and sends the diner a finished plate. The client never sees the fridge.
A Server Component runs on the server, once, and streams HTML. It can read the database, hold secrets, and pull whatever it needs without shipping any of that to the browser. What the user downloads is the result, not the machinery.
A Client Component is the part of the meal the diner finishes at the table. The sizzling plate. The thing you interact with. State, effects, event handlers, anything that reacts to a click, lives here and ships as JavaScript.
// Server Component (the default). Runs on the server, ships no JS.
async function Post({ id }: { id: string }) {
const post = await db.post.findById(id) // the fridge, hidden from the diner
return <article>{post.body}</article>
}"use client"
// Client Component. Ships to the browser because it needs to react.
export function LikeButton() {
const [liked, setLiked] = useState(false)
return <button onClick={() => setLiked(!liked)}>{liked ? "♥" : "♡"}</button>
}The "use client" line is the pass between kitchen and table. Everything past it goes to the browser.
The old model shipped the whole kitchen to every table. Your data-fetching code, your date library, your markdown parser, all of it got bundled and sent to the browser so it could rebuild the page there. Fast machines hid the cost. Slow phones did not.
Server Components leave the kitchen on the server. The parser, the query, the secret API key, none of it crosses the pass. The browser gets the finished HTML and only the interactive bits as JavaScript. Less to download, less to run, and your keys stay in the back.
Push the "use client" boundary as far down the tree as you can. Don't mark a whole page as client just because one button needs state. Keep the page a Server Component and hand the button its own client island.
A whole page marked
"use client"is a kitchen that sends every diner the raw ingredients. Technically it works. It also defeats the point.
The tricky part isn't either side. It's the pass between them. Two rules save most of the pain.
async, and can't read the database directly. If a client island needs data, the server hands it down as a prop or through a request. The table doesn't walk into the walk-in.Learn the seam and the rest is just React. Fight the seam and every feature feels like swimming upstream. The official docs on composition patterns are worth a slow read once the kitchen picture is in your head.
In practice my pages are Server Components by default. I reach for "use client" only when a piece genuinely needs to react: a form, a toggle, an animation, a live clock. Everything else stays in the kitchen where it's cheaper and safer. It's the same instinct behind the rest of my stack. Do the heavy work where the data lives, and ship the diner a clean plate.
No, and the overlap causes half the confusion. SSR renders your client components to HTML on the server, then hydrates them in the browser. Server Components never ship to the browser at all. You use both together.
In the Next.js App Router they're the default, so you're already using them. You opt out with "use client" when a component needs interactivity. Most of your tree can stay on the server.
Yes, that's the normal pattern: a server page renders a client island. The reverse is limited. A Client Component can't import a Server Component directly, but it can accept one as a children prop.
Passing a function as a prop across the pass, and trying to use useState or useEffect in a file without "use client". Both throw clear errors once you know the seam exists.
Server Components aren't a new framework to learn. They're a line drawn through the one you already know: heavy work in the kitchen, interactivity at the table, a clean plate in between. Draw that line well and your pages get smaller, faster, and safer almost for free.
Curious how this shapes a real build? Here's why my APIs start with Hono, and if you've got a Next.js project that feels heavier than it should, tell me what you're building.
Prabir Singh
Full-stack engineer from Mumbai. Twenty years in sales, now I build products that sell. More about me or start a project.

One language from the phone to the database. Here's why every project I take on is TypeScript end to end, and why I stopped apologising for it.

Express shaped a generation of Node APIs. Here's why my new backends start with Hono instead, and the three places it actually changes how I build.

Two React full-stack frameworks, one decision. Here's how I actually choose between Next.js and TanStack Start, and the projects where each one wins.