Trust is earned, not given

A different perspective

2023-03-14 · Projects

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

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.