The new default
In the App Router, every component is a Server Component unless you opt out. This is the inverse of older React: instead of "everything in the browser unless you SSR it," you have "everything on the server unless you mark it as Client."
What that means in practice
- You can write
asynccomponents — they'reawaited during render. - You can read databases, files, and secrets directly — nothing leaks to the browser.
- Heavy libraries (markdown parsers, syntax highlighters, date formatters) ship zero JavaScript for the rendered output.
- The HTML you ship is the rendered output, not a hydration shell.
What Server Components can't do
No state, no effects, no event handlers, no browser APIs. The moment you need any of those, the component becomes a Client Component (next lesson).
Start every App Router component on the server and promote only the smallest node that requires state, an event, a ref, or a browser API. Mark database and secret-bearing modules with server-only so the import direction is enforced by the build instead of team memory. Server-only execution does not make every rendered value safe. A secret can still leak if you place it in JSX or serialize it into a client prop. Protect both the code location and the output data; they are different boundaries.
Build a page with a direct database read and a tiny click counter. Verify the database module is absent from client chunks, the server content remains with JavaScript disabled, and only the counter loses interaction.