Server vs Client Components (Next.js App Router): Boundaries, Hydration Cost, and Performance
A senior model for Next.js Server Components: what runs where, how client boundaries increase JS + hydration cost, and how to design component trees for performance.
Frontend Interview Team
March 01, 2026
What you’ll learn
- What Server Components are (and what they’re not)
- What a client boundary really costs
- How hydration cost shows up as long tasks and hurts INP
- A practical decision framework for “should this be client?”
The mental model
In Next.js App Router:
- Server Components run on the server. They can fetch data and render HTML without shipping component JS to the browser.
- Client Components ship JS to the browser and can use hooks/state.
A "use client" file creates a client boundary.
The key tradeoff
More client boundaries → more JS shipped + more hydration work → worse INP risk.
A practical rule: keep "use client" low in the tree
Bad (client boundary high):
// app/page.tsx
'use client';
export default function Page() {
return (
<Layout>
<Hero />
<Content />
<FilterPanel />
</Layout>
);
}Now everything under Page becomes client.
Better: isolate interactivity:
// app/page.tsx (server)
export default async function Page() {
const data = await fetchData();
return (
<Layout>
<Hero />
<Content data={data} />
<FilterPanelClient />
</Layout>
);
}
// FilterPanelClient.tsx
'use client';
export function FilterPanelClient() {
// hooks/state here
}Interview questions
Q1) What’s the difference between SSR and RSC?
30-second answer: SSR renders HTML on the server but still often hydrates a client React tree. RSC lets you render components on the server without shipping their JS to the client at all.
Q2) Why can hydration hurt INP?
30-second answer: Hydration is main-thread work (parsing/executing JS, attaching listeners, running effects). If it creates long tasks, interactions are delayed.
Quick recap
- Server components reduce client JS
- Client boundaries have real cost
- Keep
"use client"low and isolated