Contents
22 Chapter

Why Next.js and Server Components

The limits of client-side React, the problems Server Components address, and the differences between CSR, SSR, and RSC. The pivot chapter of the whole book.

Chapter 21 wrapped up Part 3 (React with TypeScript). Part 1 covered components / props / state / events / forms, Part 2 covered effects / Context / hooks / performance / routing, and Part 3 layered a type safety net over every piece of code. Part 4 begins with this chapter. It is the pivot point of the book.

Every React example you have seen so far has been client-side. The RSC model we previewed at the end of Chapter 21 — fetching directly on the server, with the client’s useEffect + fetch almost disappearing — gets its background organized here in one place. There is almost no code. We make the case why the new model is needed and what it actually solves, because that is decisive for understanding Chapters 23 through 27.

CSR — what we have been doing all along #

Every React app we built in Parts 1 through 3 was a CSR (Client-Side Rendering) app.

CSR flow
1. Browser receives empty HTML (just a shell with <div id="root"></div>)
2. Downloads the JavaScript bundle
3. Runs the JavaScript → React paints the screen
4. Fetches data when needed → repaints

The upsides are clear.

  • Once loaded, page transitions are fast (the SPA advantage covered in Chapter 15)
  • The server only has to host static files (deployment is simple)
  • It handles interaction-rich UIs well

But the downsides are just as clear.

Downside 1. The first screen is slow to appear #

The browser receives a blank page, downloads JavaScript, runs it, and only then paints the screen. On slow networks or weak devices, the white screen lingers. This is exactly where the LCP (Largest Contentful Paint) metric we will dig into in Chapter 31 (performance · bundle · Web Vitals) takes a direct hit.

Downside 2. SEO is difficult #

Search engine crawlers either do not run JavaScript or run it late. If they only see empty HTML, they cannot index your content and your search visibility suffers.

Downside 3. The JavaScript bundle grows #

Every bit of code needed to paint the screen — components, libraries, data-handling logic — has to be downloaded to the client. Every library you add inflates what the user has to receive.

Downside 4. Data fetching becomes a waterfall #

Server response → JS download → JS execution → fetch → another fetch. The waits stack up at every step. The useFetch pattern from Chapter 21 (fetch and API response typing) is essentially one face of this waterfall.

SSR — building the HTML on the server #

The classic way to address these issues is SSR (Server-Side Rendering). It is a return to the traditional web server model used by frameworks like PHP or Ruby on Rails.

SSR flow
1. Browser requests the page
2. The server runs React to build the HTML
3. Sends the finished HTML (content is already in it)
4. Browser displays that HTML
5. Receives the JS bundle in parallel to activate interactions (hydration)

The term hydration shows up here. It means “attach JS event handlers to the already-painted static HTML to turn it into living components”. The metaphor is pouring the water of JS onto dried HTML to bring it back to life.

SSR solves the first-screen-speed and SEO problems. The catch is that all the component code still has to be sent to the client to hydrate, so the bundle size problem remains.

RSC — enter React Server Components #

This is where the React team takes another step. “What if some components don’t even need to go to the client in the first place?”

Think of the body of a blog post. Once it is rendered, there is not much for the user to click or type into. It just fetches data and prints it to the screen. For a component like that, you can execute it only on the server and ship only the HTML (or something close to it) to the client. There is no reason to send the component’s JavaScript code to the client at all.

This is the core idea of React Server Components (RSC).

RSC flow
1. Browser requests the page
2. Server runs the Server Components
3. Server Components can call the database / APIs directly
4. The result is sent in a serialized form (HTML + markers for client-component positions)
5. The client displays the received HTML + hydrates only the Client Components

What RSC addresses.

  • Smaller bundles — the code for static components never reaches the client
  • Simpler data fetching — a Server Component can just await db.query(...) (no network round trip)
  • Protected secrets — API keys and DB credentials are never exposed to the client
  • SEO and first-screen speed — the SSR advantages remain

The “RSC model preview” at the end of Chapter 21 sits on top of this exact flow.

So what runs where #

This is the key concept that will keep coming back in Part 4. Picture it before you read on.

Component kindWhere it runsAvailableNot available
Server ComponentsServer only (once)DB access, env vars, fs module, async / awaituseState, useEffect, event handlers, browser APIs
Client ComponentsServer (SSR) + client (hydration)useState, useEffect, events, browser APIsDB access (security), fs

The two kinds coexist within the same page. The static content lives in Server Components; the parts that need interaction (forms, toggles, dropdowns) live in Client Components. Drawing this boundary well is the heart of modern React app design. Chapter 24 (Server vs Client Components) takes this up in earnest.

What React 19 adds to this model #

With React 19 released, new tools that work on top of RSC have stabilized. We will meet them one by one through Part 4.

  • use() hook (Chapter 26) — a hook that can resolve both Promises and Context. The key piece for unwrapping a Server-made Promise on the Client.
  • Server Actions (Chapter 27) — calling server functions directly from <form action={serverFn}>. The new standard for forms and mutations.
  • useActionState, useFormStatus, useOptimistic (Chapter 27 + Chapter 28) — hooks that pair with Server Actions.
  • Asset Loading (preload, preconnect) — fits naturally with RSC streaming.

Chapter 28 (React 19 features) collects all of these new features in one place. Whenever a new feature shows up in Part 4, you will see a cross-link to Chapter 28.

The Next.js relationship #

React Server Components are a React feature, but to actually use them you need the build toolchain and the server infrastructure. Someone has to serialize RSC payloads, handle routing, manage caching, and run the server.

Next.js is the meta-framework that collaborates most closely with the React team and the most mature option for first-class RSC support (other options like Remix, Waku, and TanStack Start exist as well). Part 4 uses Next.js 15. The Part 6 capstone (a fullstack Todo app) is also built on Next.js 15.

Note
The SchoolofWeb site (schoolofweb.net) currently runs on Hugo, but before that it was built with Next.js. Because we have actually built a site once using the model in Part 4, this book is not a simple summary of the official docs but a perspective shaped by operational experience.

The pivot of the shift #

Before stepping into Part 4, there are two mindset shifts that matter most.

1. Always ask “where does this code run?” #

Up to now we lived in a single environment — “all code runs in the browser”. In modern React, server-running code and client-running code can be mixed in the same file. If you do not stay aware of this boundary you will get confused fast.

Ask yourself every time: “Does this function run on the server or on the client?” That question makes it clear how data fetching should work, whether you can read env vars, and whether an event handler can be attached.

2. The default is Server Component; Client Component only where needed #

When you create a new component, the default is a Server Component (in Next.js App Router). You only convert to a Client Component when there is a clear reason — state, events, browser APIs — by adding the 'use client' directive. This default is the key to keeping the bundle small.

Relation to Appendix A — for readers coming from old React #

The model in Part 4 behaves differently from old React (class components / Pages Router / Redux-only / useEffect + fetch). The procedure for moving an old codebase to the model in this part is collected separately in Appendix A (Migrating old React code). As you read Part 4, whenever an old pattern crosses your mind, keep Appendix A on the side.

What this part will cover #

Chapters 23 through 28 step through the following.

  • Chapter 23 Starting a Next.js project, the App Router file structure, the layout system
  • Chapter 24 The 'use client' directive and the server / client component boundary
  • Chapter 25 The await fetch(...) pattern in Server Components, caching
  • Chapter 26 Suspense and loading.tsx, handling loading with the use() hook
  • Chapter 27 Form submissions and mutations with Server Actions
  • Chapter 28 React 19 features in one catalog — Actions / useFormStatus / useOptimistic / use() / React Compiler / ref as prop

Then in the Part 6 capstone (Chapter 34) we tie every tool from Part 4 into a single fullstack Todo app.

Exercises #

Since this chapter is mostly conceptual with very little code, the exercises are meta as well.

  1. Pick one web app you have built (or one you know well), and split its pages / components into two groups: “this fits Server Component” / “this fits Client Component”. Use “does this component have interaction (state, events, browser APIs)?” as the dividing line.
  2. Draw a small comparison table for CSR / SSR / RSC across three metrics: (a) time to first paint, (b) SEO friendliness, (c) client bundle size. Once the trade-offs of the three models are in your head, Part 4 reads much lighter.
  3. Compare the code from Chapter 21’s final section (RSC preview) with the useFetch code from the same chapter one more time. Walk through where useState / useEffect / the cancelled flag / the loading branches went or got replaced — once you have done that comparison once, stepping into Part 4 becomes much smoother.

In one line: CSR gives fast interaction but slow first paint, weak SEO, and a large bundle. SSR fixes first paint and SEO but still ships all the code to the client. RSC runs static content only on the server, and the code itself never reaches the client. The two mindsets for Part 4 — (1) always ask “where does this code run?”, (2) default to Server Component, switch to Client Component only when needed.

Next chapter #

In the next chapter, Chapter 23 Starting Next.js and the App Router, we actually create a Next.js 15 project and get hands-on with App Router’s file-based routing and layout system. We will compare it side by side with Chapter 15’s React Router to see how the same problem (URL → screen) is solved in a different way.

X