What React does not do
React renders components. Everything else a real site needs — routing, data loading, HTML for the first paint, bundling, caching, images, metadata — is yours to assemble. Next.js is the assembly, with opinions.
| Plain React (Vite) | Next.js |
|---|---|
| You pick a router | Routes are folders |
An empty <div id="root"> is served | HTML is rendered on the server |
| Data loads after the JavaScript does | Data loads while the HTML is being made |
| One bundle, code-split by hand | Split per route by default |
| No server | Server components, route handlers, middleware |
| You configure everything | You configure less, and follow conventions |
The four rendering strategies
They are not a setting for the whole application. In the App Router each route — even each part of a page — picks its own, and a static shell can stream a dynamic section into itself.
| Strategy | HTML is built | Right for |
|---|---|---|
| Static (SSG) | At build time | Marketing pages, docs, a blog |
| Incremental (ISR) | At build, then re-made every N seconds | A catalogue that changes hourly |
| Dynamic (SSR) | On every request | Anything personal, or data that must be current |
| Client (CSR) | In the browser, after load | A dashboard behind a login, an editor |
Choosing one
- Does the content depend on who is asking? → dynamic.
- Must it be up to the second? → dynamic.
- Does it change occasionally and is the same for everyone? → static with revalidation.
- Does it never change between deploys? → static.
- Is it behind a login and irrelevant to search engines? → client rendering is fine, and simpler.
Reading cookies(), headers() or searchParams in a server component makes that route dynamic — no cache, a render per request. It is the most common reason a "static" site is slow, and the build output tells you which routes it happened to.
What you get for it
- Routing from the file system — a folder is a URL, with nested layouts.
- Server components — components that run only on the server and ship no JavaScript.
- Data on the server — fetch in the component, with caching and revalidation built in.
- Server actions — a form that calls a server function without you writing an endpoint.
- Streaming — send the shell immediately, stream the slow parts as they resolve.
- The plumbing — images, fonts, metadata, sitemaps, bundling, compression.
What it costs
- A bigger mental model: two runtimes, a boundary between them, and a cache with several layers.
- Opinions you now live inside. Going against them is harder than in a bare React app.
- A build that is slower, and a deployment that expects a Node server or an adapter.
- Version churn: the App Router changed how much of this works, and the documentation you find may be for the old one.
When plain React is the better answer
An internal tool behind a login. A dashboard. An editor. Anything where nothing needs indexing, the first paint can be a spinner, and the team is small. Vite plus React Router is less to learn, faster to build, and deploys to static hosting.
Reach for Next.js when the first paint matters to a stranger, when search engines matter, or when you want the server and the client in one codebase.
// public, content-heavy, SEO matters → Next.js
// private, app-like, behind a login → Vite + React Router
// a marketing site with no application → Astro, or plain HTML
// a mobile app with a shared API → React Native + your own API