Server Components
Trailhead's trail page reads from a database using a connection string that must never leak. In a classic React app, anything a component touches ends up in the browser bundle, so you would have to build a separate API just to hide it. In the App Router the component itself can stay on the server.
The rule
In
app, components run on the server unless you say otherwise.
Every component in the App Router is a React Server Component by default. That means:
- It runs only on the server: at build time for static routes, or per request for dynamic ones.
- It can be
asyncand await data directly: a database query, a file read, a fetch with a secret key. - Its code is not sent to the browser. The browser receives the rendered HTML plus a compact description of the output (the RSC payload), not the component's JavaScript.
// app/trails/[slug]/page.tsx (a server component: no directive needed)
import { db } from "@/lib/db"; // uses process.env.DATABASE_URL
import { marked } from "marked"; // a Markdown library
export default async function TrailPage({ params }) {
const { slug } = await params;
const trail = await db.trail.findUnique({ where: { slug } });
return (
<article>
<h1>{trail.name}</h1>
<div dangerouslySetInnerHTML={{ __html: marked(trail.notes) }} />
</article>
);
}Neither the database client, the connection string nor the Markdown library reaches the visitor. (Here trail.notes is written by Trailhead's own editors; never render untrusted HTML this way.)
The price: because a server component never runs in the browser, it cannot:
- hold state or run effects (
useState,useEffect,useReducer), - attach event handlers (
onClick,onChange), - use browser-only objects (
window,document,localStorage), - use React context as a provider or consumer.
Those jobs belong to client components, the next lesson.
The shortcut
For each component, ask "Does it need to react to the visitor in the browser?" Clicks, typing, state, animations, browser storage: it must be a client component. Only showing data, even data loaded with secrets: keep it a server component.
TrailHeader (name, distance, photo)
Only shows data. Server component.
The trap is adding useState to a server component and being puzzled by the error. The error is right: state needs the browser. Move that piece into a client component.
Drills
Answer fast — trust the rule.
In the App Router, what is a component with no directive?
Which can a server component do?
What does the browser receive from a server component?
Why is a heavy Markdown library cheaper inside a server component?
Remember
- In
app, components run on the server unless you say otherwise; they can be async and use secrets. - Shortcut: "does it react to the visitor in the browser?" If not, keep it on the server.
- Trap: state, effects, event handlers and browser objects do not work in server components.
