Why Next.js?
See what Next.js adds to React for a whole app: pages from folders, HTML from the server, data, and the build, and when a plain React app is enough.
- 01Say what React leaves to youName the jobs a whole app needs beyond React: addresses, the first HTML, data, the build, server code and the extras.
- 02Read what Next.js decidedTell which files become pages, which code runs in the browser, and what the build's ○ and ◐ say about how each page is made.
- 03Choose a fitting toolCompare Next.js with React Router's framework mode (once Remix) and Astro, and tell when a client-rendered React app is enough.
One tea shop page, built two ways
Next.js is a framework built around React. A framework is a set of parts that decides how a whole app is put together, and runs your code inside it. Next.js adds the parts a whole app needs beyond React itself. To see what that means, take one page of a small tea shop: its home page. Build it twice, once as a React app that draws the page in the browser, and once with Next.js. The component is the same in both. Here it is in the React app, with its result under the code:
const teas = [ { name: "Green tea", price: 4 }, { name: "Mint tea", price: 3 }, { name: "Lemon tea", price: 3 },]; export default function Home() { return ( <main> <h1>Tea shop</h1> <ul> {teas.map((tea) => ( <li key={tea.name}> {tea.name}: ${tea.price} </li> ))} </ul> </main> );}Lines 1 to 5 list three teas. Home, on lines 7 to 20, returns the page: a heading on line 10, and on lines 12 to 16 one list item for each tea, made with map. Line 14 shows each name and price, like Green tea: $4. The result is the page React draws.
But that page isn't what this app's server sends. This React app is client-rendered, set up the way a new Vite app is: its server sends a nearly empty page, and React draws the shop in the browser once the JavaScript has arrived. React itself doesn't decide this. What the first HTML holds is up to the app around React. This is the page every live React example in this course starts from:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="main.jsx"></script>
</body>
</html>Line 9 is the whole body of the page: an empty <div id="root">. Line 10 loads the app's JavaScript. Until that JavaScript has downloaded and run, the visitor sees a blank page. So does any program that reads the HTML without running JavaScript, like the bots that make link previews in chat apps. Drawing the page in the browser like this is called client-side rendering. Rendering strategies compares it with the other ways to make a React app's first HTML.
Now the same component in a Next.js app. Next.js reads a folder called app, and a file's place there decides its address: app/page.tsx is the page for /. This example is a real Next.js 16.4.3 project. It was built with next build and run with next start, and what it sent back for / was recorded. Click a file in the tree to read it. The result under the files shows the recording.
app/page.tsxconst teas = [ { name: "Green tea", price: 4 }, { name: "Mint tea", price: 3 }, { name: "Lemon tea", price: 3 },]; export default function Home() { return ( <main> <h1>Tea shop</h1> <ul> {teas.map((tea) => ( <li key={tea.name}> {tea.name}: ${tea.price} </li> ))} </ul> </main> );}- Green tea: $4
- Mint tea: $3
- Lemon tea: $3
The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.
app/page.tsx holds the same Home, line for line. Its name ends in .tsx because a new Next.js app uses TypeScript; this code needs no types. In app/layout.tsx, the root layout, lines 3 and 4 give every page its <html> and <body>, and children is the page that goes inside. LayoutProps<"/"> on line 1 is a type that Next.js writes for you; it says the layout gets children. The three files marked create-next-app are the ones Next.js's project starter makes around your code, and their settings will make sense later in the course.
Open the Network tab. The HTML that arrived already holds <h1>Tea shop</h1> and an <li> for each tea: Next.js ran Home on the server during next build, and saved its HTML. (The <!-- --> between a tea's name and its price is a comment React adds between two pieces of text, so it can tell them apart later.) The visitor sees the teas the moment the HTML arrives, before any JavaScript runs. Then open the Terminal tab: ○ /, and the line under it, say the page was prerendered as static content. Its HTML was made once, ahead of time.
/. The client-rendered React app's holds an empty <div id="root">, which React fills in the browser. The Next.js app's already holds the heading and the three teas, because Home ran during next build.A good cook can make any dish at home. Serving a hundred guests needs a kitchen too: a station for each job, a menu that says what's available, soups made in the morning so they're ready the moment someone orders, and steaks cooked only when a guest asks. The restaurant decides how the kitchen runs. The cook still does the cooking.
- In real life: The cook, who knows how to make each dish
- In Next.js: React, which turns components and data into a page
- In real life: The kitchen: stations, a menu, and a way orders come in
- In Next.js: Next.js: folders for addresses, a build, and a server
- In real life: Soups made in the morning, ready the moment someone orders
- In Next.js: Pages prerendered as static content:
○ - In real life: A steak cooked only when a guest orders it
- In Next.js: A part made for each request:
◐
Next.js is a React framework: React for the components, plus the other parts a whole app needs, already chosen and connected. You write React components in files, and a file's place in the app folder decides its address. Next.js makes each page's HTML on the server, during the build or for each request, and sends the browser the JavaScript for the parts that run there.
What React leaves to you
React's job is the user interface: it turns components and data into a page, and keeps the page matching the data (Why React? shows how). A whole app needs more than that. These are the jobs React leaves for you to choose a tool for, and to connect:
- Addresses (routing). Which component shows for
/cart, and what the Back button does. React has no router; you add one, such as React Router (Routing). - The first HTML (rendering). React leaves this to you. The client-rendered app above sends an empty page. HTML with the content in it needs a server that runs your components, or a build that runs them ahead of time, and you set that up yourself.
- Data. Where it comes from, and when the request starts. The small example below asks for it in an Effect, after the first render. react.dev suggests a framework or a data library for this (Fetching data).
- The build (bundling). Turning your many files into a few that browsers load fast, and sending each page only the code it needs. A build tool like Vite does part of it (Setting up a React project).
- Code that runs on the server. Reading a database, or using a secret key the browser must never see. React has Server Components and Server Functions for this, but they only work with a framework, or a bundler set up for them.
- The extras. Each page's title and description, images in the right size, fonts that load without the text jumping.
Next.js comes with all of them. Data is the easiest one to see. Here is the shop as a client-rendered React app that asks a server for its teas. This small example starts its request in an Effect:
import { useEffect, useState } from "react";import { getTeas } from "./api.js"; export default function Home() { const [teas, setTeas] = useState(null); useEffect(() => { getTeas().then(setTeas); }, []); return ( <main> <h1>Tea shop</h1> {teas === null ? ( <p>Loading teas…</p> ) : ( <ul> {teas.map((tea) => ( <li key={tea.id}> {tea.name}: ${tea.price} </li> ))} </ul> )} </main> );}Line 5 keeps the teas in state, starting as null. The Effect on lines 7 to 9 runs after the first render and calls getTeas(). In the api.js tab, a stand-in for the shop's server, line 9 answers after 600 ms. Until then, line 15 shows Loading teas…. When the teas arrive, setTeas stores them, and React shows the list. Press Run again on the result to watch it happen.
And here is the same page in Next.js, where the page loads its own data on the server:
app/page.tsximport { getTeas } from "./teas"; export default async function Home() { const teas = await getTeas(); return ( <main> <h1>Tea shop</h1> <ul> {teas.map((tea) => ( <li key={tea.id}> {tea.name}: ${tea.price} </li> ))} </ul> </main> );}- Green tea: $4
- Mint tea: $3
- Lemon tea: $3
The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.
Line 3 makes Home an async function, so line 4 can wait for getTeas() from app/teas.ts, which stands in for a database. Home is a Server Component: React's kind of component that runs only on the server. In Next.js, components start as Server Components; the next section shows how a file marks the part that also runs in the browser. There's no state, no Effect and no Loading teas…: by the time the HTML leaves the server, the teas are in it, as the Network tab shows.
Step through both apps below. Each step is one thing that happens between the visitor typing the address and the page working. Before you start, guess: after which step does each visitor see the teas?
Client-rendered React appafter step 1A blank page
React isn't running yet: clicks do nothing.
The page isn't complete yet.
The visitor types the shop's address. The browser asks the shop's server for the page at /.
In the client-rendered React app, the teas show after step 7: the browser waits for the HTML, then for the JavaScript, then for the data, one after the other. In the Next.js app, they show after step 2, in the HTML. Both apps end with React running in the browser. In the Next.js app, that last step is called hydration: React attaches the Client Components and their event handlers to the HTML that's already on the screen. Next.js also sends React a description of what the Server Components rendered, called the RSC payload, but never their code (Hydration goes further).
Each row is one job a whole app needs: what you do in a client-rendered React app like this lesson's, and what Next.js does.
| Job | In a client-rendered React app | In Next.js |
|---|---|---|
| Choosing the page for an address | You add a router library, such as React Router, and list the routes. | Folders in app are the addresses, and a page.tsx file makes a page. |
| The first HTML | Up to you. A client-rendered app, like this lesson's, sends an empty <div id="root">. | The page's HTML, made during the build or for each request, with the content in it. |
| Loading a page's data | Up to you. This lesson's example asks for it in an Effect, after the first render. | Server Components wait for it on the server, while they render. |
| Building and splitting the code | A build tool like Vite bundles it. Splitting it per page is up to you. | next build bundles it with Turbopack into shared and route-specific chunks, so each route loads the chunks it needs. |
| Code that runs on the server | A separate server or API that you write and host. | Server Components and Server Functions, in the same project. |
| Titles, images and fonts | Libraries you pick, or code you write. | Built in: the Metadata API, next/image and next/font. |
You can do each job yourself, with a library for each, and many teams do. Then you also choose them, connect them and keep them working together. React frameworks, in the React course, shows what happens when two of them aren't connected: a page whose data can't start loading until its code has arrived.
What a framework decides for you
A library waits for your code to call it: your main.jsx calls React's createRoot, and React draws what you give it. A framework works the other way around. It decides how the app is put together, and it calls your code at the right moment: you put files where it expects them, export what it expects, and it does the rest. Each row asks the same question of both:
| Question | A library, like React | A framework, like Next.js |
|---|---|---|
| Who calls whom? | Your code calls React: createRoot(…).render(<App />). | Next.js calls your code: it finds page.tsx and layout.tsx and renders them. |
| Who decides where files go? | You do. Any folder layout works. | Next.js does. Pages live in app, one folder for each part of the address. |
| Who decides how a page is made? | You do, with the server you set up. | Next.js does, from what the page's code reads. |
| What do you get in return? | Freedom to choose, and the work of connecting every part. | Parts that already work together, and rules to follow. |
Here is the shop with two pages, a layout with links, an Add to cart button and a cart. Click through its files, then look at the result's three tabs.
app/page.tsximport { getTeas } from "./teas";import AddToCart from "./add-to-cart"; export default async function Home() { const teas = await getTeas(); return ( <main> <h1>Tea shop</h1> <ul> {teas.map((tea) => ( <li key={tea.id}> {tea.name}: ${tea.price} <AddToCart id={tea.id} /> </li> ))} </ul> </main> );}- Green tea: $4
- Mint tea: $3
- Lemon tea: $3
The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.
Folders become addresses
Next.js reads the app folder. Each folder is a part of an address, and a page.tsx file makes that address a page: app/page.tsx is /, and app/cart/page.tsx is /cart. Other files sit beside them without becoming pages: app/teas.ts and app/add-to-cart.tsx have no address. You never write a list of routes. The Terminal tab shows the list Next.js made: /, /cart, and /_not-found, the page it adds for addresses that don't exist.
In app/layout.tsx, line 10 links the two pages with Link from next/link. Click Cart in the recorded page, and the browser frame switches to the recorded /cart. In the real app, a Link swaps the page in place, without reloading it. When the link comes on the screen, Next.js already loads the linked page's App Shell: the part that's the same for every visitor. The rest can wait for the click. Line 3 of the layout gives every page its title: the frame shows Tea shop next to the address.
Server Components first, the browser when you say so
Home and CartPage are Server Components: their code never goes to the browser. The Add to cart button needs the browser, because it reacts to clicks and keeps added in state. So app/add-to-cart.tsx starts with "use client" on line 1. That line marks where the client part of the app begins: AddToCart, and whatever its file imports, also goes to the browser. Components like it are Client Components. Next.js still renders their HTML on the server, and React makes the button work in the browser. Line 12 of app/page.tsx puts one next to each tea. (Server Components explains the two kinds.)
The recording shows the split: the JavaScript files the home page loads contain the text Add to cart, and the cart page's files don't, because that page has no button. When the button is clicked, line 9 of app/add-to-cart.tsx stores the tea's id in a cookie: a small piece of text the browser keeps for a site and sends back with every request to it.
How each page is made
Open the Terminal tab again. After building, Next.js lists every route with a symbol, and the legend under the list says what each symbol means:
○ /, Static: prerendered as static content. In this build, nothing in the home page depends on the visitor, so its HTML was made once, during the build.◐ /cart, Partial Prerender: prerendered as static HTML with dynamic server-streamed content. The page's static shell, with its heading, was made during the build.CartItemreads the visitor's cookie on line 6 ofapp/cart/page.tsx, so that part is made for each request and streamed in.
You never said which page is which. Next.js worked it out from the code: a cookie only exists when a request comes in, so it can't be read during the build. The Suspense around CartItem, on lines 15 to 17, marks the part that may wait. The rendering lessons later in the course explain both.
The Network tab shows the difference too. A cache is a server that keeps a stored copy of a page, and a CDN is a network of them, close to visitors. / comes with Cache-Control: s-maxage=31536000, which lets a shared cache like a CDN keep the page for up to a year. /cart comes with private, no-cache, no-store…: nobody may keep a copy, because each visitor gets their own. Pick /cart, after Add to cart on Mint tea, above the page: the same address, asked for with the cookie cart=mint, now says In your cart: Mint tea.
Try it below. Each button is one version of app/cart/page.tsx, and each was built with real Next.js. Before you click, guess what the build prints for /cart.
app/cart/page.tsxNext.jsapp/cart/page.tsxexport default function CartPage() { return ( <main> <h1>Your cart</h1> <p>Your cart is empty.</p> </main> );}$ next build▲ Next.js 16.4.3 (Turbopack)- Cache Components enabled- Partial Prefetching enabled Creating an optimized production build ...✓ Compiled successfully Running TypeScript ... Finished TypeScript ... Collecting page data ...✓ Generating static pages (4/4) Finalizing page optimization ... Route (app)┌ ○ /├ ○ /_not-found└ ○ /cart ○ (Static) prerendered as static contentThis cart page reads nothing from the request: line 5 is the same for every visitor. So Next.js makes all of its HTML during the build, and prints ○ /cart, like the home page.
The first version reads nothing from the request, so the whole page is made during the build: ○ /cart. The second reads the cookie inside Suspense: ◐ /cart. The third reads it with nothing to show while it waits, and Next.js refuses to build, with an error that names the fix. That's what a framework decides for you: you write the page, and Next.js works out when its HTML can be made, and checks that the page works the way it decided.
app folder: each page.tsx is an address, and the other files aren't pages. From each page's code it decides when the HTML is made. / is prerendered as static content (○): in this build, made once. /cart is a static shell with a part made for each request, because it reads a cookie (◐).The build, and the extras
next build also bundled the code with Turbopack, Next.js's bundler, into chunks: files under /_next/static/chunks/ that the HTML's <script> tags load. Some chunks are shared by every route, and some belong to one route, so each route loads the chunks it needs. Other jobs from the list have parts of their own: the Metadata API for titles and link previews, like the metadata on line 3 of the layout, next/image for images in the right size, and next/font for fonts that don't make the text jump. Each one has a lesson later in the course.
Next.js, Remix and Astro
Next.js isn't the only framework. Two other names come up in almost every comparison, and both have changed a lot, so this is where they stand on October 29, 2026, from each project's own docs and from npm.
Remix, which is now React Router
Remix was a React framework known for loaders: a function next to each route that loads its data on the server before the page renders. In November 2024 its React features moved into React Router 7, as React Router's framework mode; React Router's upgrade guide calls version 7 the next major version of Remix after version 2. React Router 8 came out in June 2026, and the latest is 8.5.1. A route there looks like this:
import { getTeas } from "../teas";
export async function loader() {
return { teas: await getTeas() };
}
export default function Home({ loaderData }) {
return (
<ul>
{loaderData.teas.map((tea) => (
<li key={tea.id}>{tea.name}</li>
))}
</ul>
);
}Lines 3 to 5 are the route's loader. It runs on the server before the page renders, and line 7 gets what it returned as loaderData. The routes are listed in one file, app/routes.ts, instead of folders. In this standard framework mode, the route components also run in the browser, where React hydrates the whole page. React Router also has a Server Components mode, still marked unstable. The React frameworks lesson shows more of it.
The name lives on in Remix 3, released in October 2026 (3.1.0 on npm): a new full-stack framework from the same team that isn't built on React. It has components of its own and keeps state in plain JavaScript variables, without hooks. For React projects, the former Remix features now live in React Router's framework mode.
Astro, for content sites
Astro is a framework for content-driven sites: blogs, docs, marketing pages and shops. It renders pages to HTML on the server or during the build, and by default sends no JavaScript from a UI library like React for them. (A page can still include scripts of its own.) Its pages are .astro files, and with Astro's React integration they can use React components, as they can use Vue, Svelte and others. A React component stays plain HTML unless you mark it with a client: directive. Then it becomes an island: a small part of the page that loads its own code and runs in the browser, on its own.
---
import { getTeas } from "../teas";
import AddToCart from "../components/AddToCart.jsx";
const teas = await getTeas();
---
<h1>Tea shop</h1>
<ul>
{teas.map((tea) => (
<li>{tea.name} <AddToCart client:load id={tea.id} /></li>
))}
</ul>Lines 1 to 5, between the --- lines, are the page's script: it runs on the server, and line 4 waits for the teas. Lines 6 to 11 are the page's HTML. On line 9, AddToCart is a React component, and client:load makes it an island whose code loads with the page. Without client:load, Astro would send its button as HTML with no code, and the button would never respond. The latest Astro is 7.4.4.
client:load; a page can still add scripts of its own.Each row asks one question of the three frameworks, as their docs answer it.
| Question | Next.js | React Router (framework mode) | Astro |
|---|---|---|---|
| What is it built for? | Full-stack React apps, with the pages and the server code in one project: shops, dashboards, sites with accounts. | React apps of any kind, from a browser-only app to full server rendering. | Content sites: blogs, docs, marketing pages and shops. |
| How are routes made? | Folders in app, with a page.tsx in each. | A list of routes in app/routes.ts. | Files in src/pages. |
| Where does a page's data load? | In Server Components, while they render on the server. | In each route's loader, before the page renders. | In each page's script, on the server or during the build. |
| What JavaScript goes to the browser? | React, Next.js's router, and the Client Components. Server Components send none of their code. | In its standard framework mode: React, the router, and every route component on the page. (Its Server Components mode is still unstable.) | No UI framework code by default. Each island marked client: sends its component's code, and a page can add scripts of its own. |
| Does it use React? | Yes. It's built on React. | Yes. It's built on React. | If you want: React is one of several UI libraries it can use. |
| Latest version on October 29, 2026 | 16.4.3 | 8.5.1 | 7.4.4 |
All three send HTML with the content in it, so all three fix the empty page from the overview. They differ in how much of the page runs in the browser afterwards, and in what they're built for. This course teaches Next.js: react.dev lists it first among the frameworks it recommends, and says its App Router fully implements React's Server Components.
When a plain React app is enough
A framework adds parts, and with them more to learn and decisions that are made for you. React's own docs recommend starting a new app with a framework, and also explain how to build one without. A plain React app here means a client-rendered one: React with a build tool like Vite, the libraries you choose, and an empty first page, like the one in the overview. It's often the simpler choice when:
- Everything is behind a login, and the data has an API. A dashboard for the shop's staff: search engines and link previews never see it, its data comes from an API (the addresses on a server that send data) that already exists, a blank page for a moment on the first visit is fine, and no page has to be made for each request.
- React is one part of a bigger page. A cart widget inside a site built with something else, where React draws into one element.
- The app lives in the browser. A drawing tool or a game that works offline, with no server data to load.
- You're learning React. Fewer parts means everything that happens is in your own code. That's why every live example in the React course is plain React.
Choose a framework when the first HTML matters (pages that search engines and link previews read, or that must show quickly on slow phones), when pages need data from a server, or when the app will grow to many pages. That's when you would otherwise build the framework's parts yourself.
Some sites must be plain files on a host that runs no code of yours. Next.js can build those too: with a static export, it writes the HTML of every route it knows at build time into a folder of files. What needs a server doesn't work there: reading cookies, or anything else at request time, and dynamic routes whose addresses aren't known during the build. The site you're reading is a Next.js app, exported this way.
A shop, a blog and a dashboard
Real projects mix these needs. Here are three the tea shop could have, what each needs, and a good fit for each. Each row is one project:
| Project | What it needs | A good fit |
|---|---|---|
| A tea shop | Product pages that search engines can read, a cart for each visitor, and checkout on the server. | Next.js: static product pages, a cart made for each visitor, and server code for checkout, like the shop in this lesson. |
| A tea blog | Posts written once and read many times, fast on phones, with a few interactive parts. | Astro, which sends no UI framework code unless a part needs it, or Next.js with static pages if the blog is part of a bigger React app. |
| The shop's stock dashboard | Staff only, behind a login, lots of clicking and typing, data from an API (the server's addresses that send data) that already exists. | A client-rendered React app with Vite and a router: a blank first moment is fine, and no page has to be made for each request. |
None of these is the only right answer: a team that knows Next.js well might build all three with it. What matters is that each choice comes from what the project needs. Sort these needs: which ones point to a framework?
- Search engines must read every product page.
- Chat apps must show a post's title in their link previews.
- Only signed-in staff use it, its data comes from an API that already exists, and a blank first moment is fine.
- It's one widget inside a page built with something else.
- Pages need data from a database the browser can't reach.
- It's a drawing tool that works offline, all in the browser.
- The site will grow to hundreds of pages.
- You're learning how components and state work.
Sort each need: does a framework help with it, or is a client-rendered React app enough?
Common mix-ups
- "Next.js is a different React." No. Its pages, layouts and components are React components. Next.js decides how they're built, rendered and sent.
- "Next.js sends no JavaScript." No. React and Next.js's router still load and hydrate the page, and Client Components send their code. Only Server Components keep their code on the server.
- "Every Next.js page is made again for every request." No. In the shop,
/was made once, during the build (○). Only the part of/cartthat reads the cookie is made for each request. - "You have to tell Next.js which pages are static." No. It works it out from what each page reads, and prints its decision after the build.
- "Next.js always needs a server." No. A site whose routes are all known during the build, and that reads nothing at request time, can be exported as plain files. This site is.
- "Remix is gone." No. Its React features are React Router's framework mode, and Remix 3 is a new framework that doesn't use React.
- "Astro can't use React." It can. React components render to HTML there, and the ones marked
client:run in the browser as islands.
Practice exercises
These exercises are about Next.js projects, which can't run inside this page. Answer first, then use the hints.
This is the page the React app's server sends. What's inside the root element when it arrives?
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="main.jsx"></script>
</body>
</html>Nothing: line 9 is <div id="root"></div>, with nothing between the tags. The teas only appear after main.jsx and React load and Home runs in the browser, which is why the visitor first sees a blank page.
These are the tea shop's files. Which addresses does next build list for them, besides /_not-found?
app/
cart/
page.tsx
add-to-cart.tsx
layout.tsx
page.tsx
teas.ts/ and /cart. app/page.tsx is / and app/cart/page.tsx is /cart. add-to-cart.tsx, layout.tsx and teas.ts aren't pages. The recorded build of the tea shop lists ○ /, ○ /_not-found and ◐ /cart.
Write app/about/page.tsx for the tea shop, with a heading that says About us. Which new line does the build's route table get?
// app/about/page.tsx
export default function AboutPage() {
// Return a <main> with a heading, "About us"
}export default function AboutPage() {
return (
<main>
<h1>About us</h1>
<p>We sell three teas from one small shop.</p>
</main>
);
}
The folder about gives the address /about, and the page reads nothing from the request, so Next.js makes it during the build. The recorded build of the shop with this file lists ○ /about, between ○ /_not-found and ◐ /cart.
This version of app/cart/page.tsx shows the same message to everyone. Which symbol does the build print next to /cart?
export default function CartPage() {
return (
<main>
<h1>Your cart</h1>
<p>Your cart is empty.</p>
</main>
);
}
○. This cart page reads nothing from the request, so Next.js prerenders all of it during the build, like the home page. It's the first version in the playground, and its recorded build prints ○ /cart.
The tea shop's build fails with this file as app/add-to-cart.tsx: You're importing a module that depends on useState into a React Server Component module. Which line is missing?
import { useState } from "react";
export default function AddToCart({ id }: { id: string }) {
const [added, setAdded] = useState(false);
function add() {
document.cookie = `cart=${id}; path=/`;
setAdded(true);
}
return (
<button onClick={add} disabled={added}>
{added ? "Added" : "Add to cart"}
</button>
);
}
"use client";
import { useState } from "react";
export default function AddToCart({ id }: { id: string }) {
const [added, setAdded] = useState(false);
function add() {
document.cookie = `cart=${id}; path=/`;
setAdded(true);
}
return (
<button onClick={add} disabled={added}>
{added ? "Added" : "Add to cart"}
</button>
);
}
"use client"; on line 1. Without it, AddToCart is a Server Component, and Server Components can't keep state or handle clicks. The recorded build fails with: You're importing a module that depends on useState into a React Server Component module. With the line back, it builds.
The tea shop wants a blog. Each post is written once and read by many people. When someone shares a post in a chat app, the link preview must show the post's title. Search engines should find every post. Each post has a Like button. Does it need a framework, or is a client-rendered React app enough?
A framework, like Next.js or Astro. Link previews and search engines read the first HTML, and a client-rendered React app sends an empty page. A framework can make each post's HTML during the build, with the title in it. The Like button still works: in Next.js it's a Client Component, and in Astro an island.
Check your understanding
For each question, think about where the code runs, and when its HTML is made.
Question 1 of 8What does a framework like Next.js do that a library like React doesn't?
Choose an answer to see the explanation.
Question 2 of 8This is what the client-rendered React app's server sends for
/. What does the visitor see before the JavaScript runs?Read the code, then predictHTML<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>React example</title> </head> <body> <div id="root"></div> <script type="module" src="main.jsx"></script> </body> </html>Choose an answer to see the explanation.
Question 3 of 8Which file is the page for the address
/cartin a Next.js app?Choose an answer to see the explanation.
Question 4 of 8The tea shop's build prints
○ /and◐ /cart. Why is/cartdifferent?Choose an answer to see the explanation.
Question 5 of 8In the tea shop, which component's code is sent to the browser?
Choose an answer to see the explanation.
Question 6 of 8This file is
app/about/page.tsxin the tea shop. What doesnext buildprint for it?Read the code, then predictTSXexport default function AboutPage() { return ( <main> <h1>About us</h1> <p>We sell three teas from one small shop.</p> </main> ); }Choose an answer to see the explanation.
Question 7 of 8What happened to Remix's React features?
Choose an answer to see the explanation.
Question 8 of 8Which project gains the least from a framework?
Choose an answer to see the explanation.
Key takeaways
- React draws the interface. A whole app also needs addresses, the first HTML, data, a build, server code and the extras; without a framework, you choose and connect a tool for each.
- React leaves the first HTML to you. A client-rendered React app's server sends an empty page, filled in after the JavaScript runs. Next.js sends HTML with the content already in it.
- A framework calls your code. In Next.js, folders in
appare addresses,page.tsxmakes a page, and components start as Server Components. A file that starts with"use client"marks the part that also runs in the browser. - Next.js decides how each page is made from what its code reads:
○, prerendered as static content, or◐, a static shell with request-time content streamed in.next buildprints each decision. - Remix's React features are now React Router's framework mode. Astro is built for content sites and sends no UI framework code except for its islands.
- A client-rendered React app is enough when nobody needs its first HTML and nothing has to be made for each request: a staff tool with an API, a widget, an app that lives in the browser.
Remember the one-liner.
React draws the page; Next.js decides everything around it, so the first response already holds the page.
Next.js's source code is on GitHub, and these links point at release v16.4.3, the version every example on this page was built with. next build starts in packages/next/src/build/index.ts, and the symbol in front of each route comes from getTreeViewSymbol, at lines 236 to 276 of build/utils.ts: ○ for a page where nothing had to wait for a request, and ◐ for one where a part did. The course's last stage, Under the hood, reads them.
Coming next: The React you need for Next.js. It gathers the React that Next.js code uses every day, like Server Components, Suspense and form actions, with links to the lessons that teach each one.