Under The Hood
frontend react React 19 (RSC stable in Next.js App Router)

React Server Components: the client/server boundary

Last updated
Prerequisites:
React component model
Next.js App Router basics
  • react
  • server-components
  • nextjs
  • serialization
  • rsc
  • architecture

Read at your depth

The practical view

In Next.js App Router, components are Server Components by default: export default async function Page() { const data = await fetch(...); return <List items={data} />; } runs entirely on the server — the async/await, the fetch, the file system access — and ships only rendered output. Files with 'use client' at the top become Client Components: their JS bundle ships to the browser and they can use useState, useEffect, and event handlers. The boundary rule: a Client Component can be rendered inside a Server Component, but props crossing the boundary must be serializable (no functions, no class instances, no Date unless wrapped in a serializable form).

The same idea in other frameworks

react equivalent
// page.jsx (Server Component, no 'use client')
export default async function Page() {
  const posts = await db.query('SELECT * FROM posts');
  return <PostList posts={posts} />;
}

Legacy vs modern

Fetching data in the client component vs in a server component

Client-side fetching ships the fetch logic, the endpoint URL, and double-loads state; a server component fetches once, server-side, and passes serializable props down.

before → after
Client component fetch
'use client';
const [posts, setPosts] = useState([]);
useEffect(() => { fetch('/api/posts').then(r => r.json()).then(setPosts); }, []);
Server component fetch
export default async function Posts() {
  const posts = await db.query('SELECT * FROM posts');
  return posts.map((p) => <li key={p.id}>{p.title}</li>);
}

Interview gotchas

Context

This question exposes whether you truly understand serialization rather than having memorized the 'use client' rule.

The mechanical answer

The RSC payload crosses the network as a serialized React Flight stream. Functions are not serializable — they close over server-side scope (the request, the database handle, secrets) and there is no standard wire format that could preserve their semantics safely. If React silently shipped functions, it would either leak server scope or break the boundary entirely. The only functions that exist on the client are those defined in client modules (event handlers inside 'use client' files), which the bundler places in the client chunk. Passing a function from a server module to a client component would require the server module to be in the client bundle, which is exactly what the boundary prevents.

Trap

The naive answer is 'functions can't be JSON serialized' — technically true but incomplete. The deeper point is that a server function's closure references the server runtime; even if you could stringify it, executing it client-side would fail or leak. Interviewers also appreciate when you mention server actions as the sanctioned way to pass a callable across the boundary — they are RPC stubs, not closures.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.