Master React Server Components — the critical RSC vs SSR distinction, what 'use client' means, what you can and cannot do in Server Components, how data fetching changes, and how Next.js App Router implements RSC.
React Server Components run exclusively on the server and never ship their JavaScript to the browser — zero bundle size. Think of the component tree as two worlds: the server world (RSC) handles data fetching, database calls, and markup generation with no client cost; the client world (marked 'use client') handles interactivity and browser APIs. The boundary is explicit — you decide where the server layer ends and the client layer begins. RSC is NOT the same as SSR: SSR converts React to HTML strings; RSC are actual React components that live permanently on the server.
This is the most common point of confusion, and interviewers will test it directly.
You can use both together: Next.js App Router uses RSC for data fetching (zero bundle cost) combined with SSR to generate HTML for fast first paints.
By default in the App Router, every component is a Server Component. The 'use client' directive at the top of a file marks it — and everything it imports — as a Client Component:
'use client'; // ← This file and its imports are Client Components
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0); // state only works in Client Components
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
The directive marks a boundary in the module graph — everything above it (imported by a Server Component) is server-only; everything below (imported by a Client Component) runs on the client.
// Server Component — async, direct DB call, no 'use client'
async function UserProfile({ userId }) {
const user = await db.user.findUnique({ where: { id: userId } }); // direct DB query
return (
<div>
<h1>{user.name}</h1>
<LikeButton postId={user.featuredPostId} /> {/* Client Component child is fine */}
</div>
);
}
Rule of thumb: if it needs the browser or interactivity → 'use client'. If it's data fetching and markup → Server Component.
In Server Components, you fetch data directly in the component body with async/await. No useEffect, no useState for loading/error, no API route needed:
// Old pattern — Client Component with useEffect
'use client';
function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts);
}, []);
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// New RSC pattern — Server Component, zero client JS
async function ProductList() {
const products = await db.product.findMany(); // direct DB, no API route
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
Server Actions (marked with 'use server') are async functions that run on the server but can be called from Client Components — like an RPC call. They're the RSC equivalent of an API endpoint, but with less boilerplate:
'use server';
export async function createPost(formData) {
const title = formData.get('title');
await db.post.create({ data: { title } });
revalidatePath('/posts'); // clear the cache for the posts page
}
// Used directly in a Client Component form — no /api/posts route needed
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
A Server Component that imports a 500KB data processing library adds zero bytes to the client bundle. The same import in a Client Component adds 500KB. This is the core bundle-size advantage of RSC — move heavy logic to the server, ship less JavaScript to the browser.
Many developers think RSC and SSR are the same thing — SSR converts React to an HTML string on the server; every component still runs on the client for hydration. RSC components run ONLY on the server and their JavaScript is never sent to the browser. They're complementary, not the same.
Many developers think 'use client' makes a component only render on the client — 'use client' means the component is a Client Component, but Client Components are still server-rendered (as HTML) on first load in Next.js. 'use client' marks the boundary where server-only React ends and interactive React begins.
Many developers think Server Components are faster because they run on the server — the performance win is reduced JavaScript bundle size and eliminated client-side data fetching waterfalls, not server execution speed. A Server Component eliminates the need for an API route, a fetch call, useState for loading state, and error handling.
Many developers try to use useState or useEffect in Server Components — these APIs require the React runtime on the client and don't exist on the server. Any component using hooks must be a Client Component (marked 'use client').
Many developers think you can't pass data from Server to Client Components — you can pass serializable data (strings, numbers, objects, arrays) as props from Server to Client Components. You cannot pass functions, Dates, or class instances because they aren't serializable across the network boundary.
Many developers think RSC requires Next.js — RSC is a React feature, but Next.js App Router is currently the most mature and production-ready implementation. Other frameworks (Remix, Waku, Parcel) are adding RSC support, but Next.js App Router is the primary context where RSC interviews are asked about.
Dashboard pages in Next.js App Router: the page component is a Server Component that fetches user data directly from the database, renders the layout and non-interactive parts, and passes data down to Client Components (charts, interactive filters) as serialized props.
E-commerce product pages: the product details, SEO metadata, and static content are Server Components (fast, no JS shipped); the Add-to-cart button, quantity selector, and reviews tabs are Client Components (interactive, need state).
Authentication-aware layouts: a Server Component reads the session cookie directly (no API call), fetches the user from the database, and conditionally renders different navigation — zero client JS needed for this auth check.
Heavy data transformations: a report component that processes megabytes of data and renders a table uses a Server Component with a server-side data library (Papa Parse, sql.js) — the heavy library never reaches the browser.
Server Actions for mutations: a blog editor uses a Server Action for 'publish post' — no /api/publish route, no client-side fetch, the action runs on the server and revalidates the post cache.
Streaming with Suspense: long-running server data fetches are wrapped in Suspense, allowing React to stream the page HTML progressively — the shell renders immediately, slow data sections stream in as they resolve, all server-side.
What are React Server Components (RSC) and how do they differ from SSR?
Reading answers is not the same as knowing them. Practice saying them out loud with AI feedback — that's what builds real interview confidence.