Introduction to Next.js
Project 7 used Next.js purely for its file-based routing, with everything else identical to a regular Vite app. Every project across all four courses has used the same rendering approach without naming it: client-side rendering (CSR) — the browser downloads a near-empty HTML file, then downloads and runs JavaScript that builds the entire page from scratch. This final chapter explains what Next.js adds on top of that, and why it matters.
The Limits of Client-Side Rendering
A pure CSR app's initial HTML is essentially just <div id="root"></div> — nothing meaningful exists on the page until the JavaScript bundle finishes downloading, parsing, and running React's first render. On a slow connection or an underpowered device, that's a real, visible delay before anything appears at all. It also means anything that reads raw HTML without running JavaScript — some search engine crawlers, social media link previews, accessibility tools — sees essentially nothing.
Server-Side Rendering and Hydration
SSR runs the React rendering logic on the server, for each incoming request, producing real, populated HTML sent straight to the browser — a user sees actual content immediately, before any JavaScript has even started downloading. The browser then downloads React's JavaScript in the background and hydrates that already-visible HTML — attaching event listeners and making it properly interactive — rather than re-building it from nothing.
Server Components vs Client Components
In Next.js's App Router, every component is a Server Component by default — it runs only on the server, and crucially, none of its code is ever shipped to the browser at all, not even as part of the JavaScript bundle. Notice HomePage is simply an async function reading data with plain await fetch(...) — no useState, no useEffect, none of Intermediate Chapter 6's loading-state machinery, because there's no client-side loading state to manage: the data is already there by the time the HTML reaches the browser.
Anything needing useState, useEffect, event handlers, or any other browser-only behavior must be marked a Client Component with the "use client" directive at the very top of the file — this opts that specific component (and anything it imports) back into the familiar CSR-style behavior covered throughout the rest of this entire course, hydrated and interactive in the browser. A typical Next.js page mixes both: Server Components for the bulk of the static, data-heavy layout, with small Client Components dropped in exactly where real interactivity is needed.
useState (or any other hook, or attaching an onClick) inside a component without "use client" fails immediately with an error, since Server Components have no browser runtime to manage that state in at all. The fix is always the same: add "use client" as the very first line of that file. A second, subtler issue — a hydration mismatch — happens when a Server Component's rendered HTML and the client's first render disagree (e.g. using Date.now() or checking window directly inside a Server Component, which doesn't exist during that initial render); React detects the mismatch and re-renders to "fix" it, but the visible flicker and console warning are a sign something rendered differently than expected between server and client.
| Strategy | When it renders | Best for |
|---|---|---|
| CSR (every project so far) | Entirely in the browser, after JS loads | Highly interactive apps; SEO/initial load less critical |
| SSR | On the server, fresh, per request | Pages whose content changes per visit/user, needing fast first paint + SEO |
| SSG (Static Generation) | On the server, once, at build time | Content that's the same for everyone (marketing pages, blog posts) |
Coding Challenges
Build a Next.js Server Component page that fetches a list from any free public API directly with async/await (no useState/useEffect at all), rendering the results.
📄 View solutionBuild a Counter Client Component (marked with "use client", using useState) and drop it into an otherwise server-rendered page alongside server-fetched content, confirming both the static content and the interactive counter work correctly on the same page.
📄 View solutionDeliberately write a component using useState without the "use client" directive, run the app, and read the resulting error message carefully. Then add "use client" and confirm it resolves.
📄 View solutionChapter 7 Quick Reference
- CSR — everything used throughout this course; renders entirely in the browser after JS loads
- SSR — server renders real HTML per request; the browser then hydrates it into an interactive app
- Server Components (Next.js App Router default) — run only on the server; their code never reaches the browser
- "use client" — required for any component using hooks, event handlers, or other browser-only behavior
- Server Components can
await fetch(...)directly — no loading state needed, since data arrives before the HTML does - A hydration mismatch happens when server and client render disagree — watch for browser-only APIs used inside Server Components