React.js, part 4: Server Components arrive — the mental model behind the App Router
Part 4from the React.js series · 20 parts in all
React Server Components (RSC) are less a feature than a change of subject. Until 2023 a React component was a function that ran in the browser. With RSC, a component may run only on the server, never ship to the browser, and still compose with client components in one tree. Part 4 builds the mental model that the Next.js App Router made default.
Two kinds of component, one tree
A server component is an async function. It can read a database, use a
secret, and render to a special format — and none of its code reaches the browser:
// app/invoices/page.tsx - a SERVER component (the default)
import { db } from '@/lib/db';
export default async function Invoices() {
const rows = await db.invoice.findMany(); // direct DB access, no API hop
return <InvoiceTable rows={rows} />; // rendered on the server
}
The browser only receives the rendered output plus whatever props cross the boundary. A client component is marked at the top of the file and is the only place hooks, state, and browser APIs exist:
'use client';
import { useState } from 'react';
export function SortToggle({ children }) {
const [desc, setDesc] = useState(true); // hooks require the client
const sorted = sort(children, desc);
return <button onClick={() => setDesc(d => !d)}>{sorted}</button>;
}
The boundary rule
Composition only flows one way: a server component may render a client component, but a client component cannot import a server component — because the client code ships to a machine that has no server. The trick that makes it feel seamless is passing a server component as children into a client one: the client component is told about the result, not the code.
What you actually get
- Smaller bundles. A Markdown renderer, a date library, a syntax highlighter — server-only dependencies stop being shipped.
- Direct data access. The "API route that just calls the database" disappears for read paths.
- Secrets stay server-side by construction, not by discipline.
The cost is a new vocabulary — server/client, serializable props, boundaries — and a
fetch model where "where does this run?" is the first question. Next: the
use hook, which is how a client component reads a promise or a context the RSC
world handed it.