cf.completefrontend
Open lab My progress
THE NEXT.JS FIELD GUIDE

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.

By the end, you can
  • 01
    Say 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.
  • 02
    Read 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.
  • 03
    Choose 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:

The shop's home page as a React appPop out in the code editor (opens in a new tab)JSX
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>  );}
ResultLoading…

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:

index.html: what this React app's server sends (its styles left out)HTML
<!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.

The same page as a Next.js appNext.js
app/
app/page.tsx
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>  );}
Recorded from Next.js 16.4.3
localhost:3000/
Tea shop
  • 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.

Two browser windows, each showing the first HTML a visitor gets for the shop's home page. Left, the client-rendered React app: only an empty div with the id root, so the page is blank until the JavaScript runs. Right, the Next.js app: Tea shop, Green tea $4, Mint tea $3 and Lemon tea $3, already in the HTML.Client-rendered React appfirst HTML<div id="root"></div>blank until the JavaScript runsNext.js appfirst HTMLTea shopGreen tea: $4Mint tea: $3Lemon tea: $3the teas are already in it
The first response for /. 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.
Real-life analogyA cook, and a restaurant kitchen

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: ◐
The main idea

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:

A React app that loads the teas after it rendersPop out in the code editor (opens in a new tab)JSX
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>  );}
ResultLoading…

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:

A Next.js page that loads the teas while it rendersNext.js
app/
app/page.tsx
import { 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>  );}
Recorded from Next.js 16.4.3
localhost:3000/
Tea shop
  • 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?

Playground: who does what when a visitor opens the shop
Who does whatClient-rendered React app
What the visitor seesafter step 1
tea-shop.example/

A blank page

React isn't running yet: clicks do nothing.

The page isn't complete yet.

Step 1 of 7
Browser: The visitor opens the shop

The visitor types the shop's address. The browser asks the shop's server for the page at /.

Each step is one thing that happens, in order. The client-rendered React app's screens are what its live example above shows; the Next.js app's are from the recording of the example above. Times aren't to scale. Reset goes back to the client-rendered app's first step.

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.

Who does each job
JobIn a client-rendered React appIn Next.js
Choosing the page for an addressYou 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 HTMLUp 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 dataUp 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 codeA 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 serverA separate server or API that you write and host.Server Components and Server Functions, in the same project.
Titles, images and fontsLibraries 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:

A library and a framework
QuestionA library, like ReactA 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.

A two-page tea shop in Next.jsNext.js
app/cart/
app/page.tsx
import { 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>  );}
Recorded from Next.js 16.4.3
localhost:3000/Tea shop
Tea shop
  • Green tea: $4 Add to cart
  • Mint tea: $3 Add to cart
  • Lemon tea: $3 Add to cart

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. CartItem reads the visitor's cookie on line 6 of app/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.

Playground: what does the build decide for /cart?
The tea shop, with this version of app/cart/page.tsxNext.js
app/cart/
app/cart/page.tsx
export default function CartPage() {  return (    <main>      <h1>Your cart</h1>      <p>Your cart is empty.</p>    </main>  );}
Recorded from Next.js 16.4.3
$ 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 content
Try it yourself
The same for everyone

This 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.

Every version was built with next build from Next.js 16.4.3; the Terminal shows what it printed, and the other tabs what next start sent back. Only app/cart/page.tsx changes. Reset goes back to the first version.

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.

An editor window for the app folder lists page.tsx, cart/page.tsx, add-to-cart.tsx and teas.ts. An arrow goes from page.tsx to a tag for the address slash, marked with a circle: prerendered as static content. An arrow goes from cart/page.tsx to a tag for slash cart, marked with a half circle: a static shell, with a part streamed in. add-to-cart.tsx and teas.ts are marked: not pages.apppage.tsxcart/page.tsxadd-to-cart.tsxteas.ts/○ prerendered as static/cart◐ static shell + streamed partnot pages: no address
Next.js reads the 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:

app/routes/home.jsx: a React Router route with a loaderJSX
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.

src/pages/index.astro: an Astro page with one React islandAstro
---
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.

The same tea shop page in three frameworks: Next.js, React Router in its standard framework mode, and Astro. Each page has a heading, three teas and an Add button for each. Shaded parts send their code to the browser. In Next.js only the Add buttons are shaded, plus React and its router. In React Router everything is shaded. In Astro only the Add buttons are shaded, as islands, plus React for them.Next.jspageTea shopGreen teaAddMint teaAddLemon teaAdd+ React and its routerReact RouterpageTea shopGreen teaAddMint teaAddLemon teaAdd+ React and its routerAstropageTea shopGreen teaAddMint teaAddLemon teaAdd+ React, for the islands
Shaded parts send their code to the browser, where React runs it; all three send the teas in the first HTML. Next.js keeps Server Components' code on the server. React Router's standard framework mode runs every route component in the browser too. Astro sends no UI framework code by default, only each island marked 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.

Next.js, React Router and Astro
QuestionNext.jsReact 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, 202616.4.38.5.17.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.

Plain files don't rule out Next.js

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:

Three projects, three choices
ProjectWhat it needsA good fit
A tea shopProduct 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 blogPosts 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 dashboardStaff 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?

Does this need 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.
Try it yourself
0 of 8 correct

Sort each need: does a framework help with it, or is a client-rendered React app enough?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

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 /cart that 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.

Exercise 1 · Warm-upRead the React app's first HTML

This is the page the React app's server sends. What's inside the root element when it arrives?

Starter codeHTML
<!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>

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFind the pages in a folder

    These are the tea shop's files. Which addresses does next build list for them, besides /_not-found?

    Starter codeText
    app/
      cart/
        page.tsx
      add-to-cart.tsx
      layout.tsx
      page.tsx
      teas.ts

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeAdd an About page

      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?

      Starter codeTSX
      // app/about/page.tsx
      export default function AboutPage() {
        // Return a <main> with a heading, "About us"
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticePredict the symbol

        This version of app/cart/page.tsx shows the same message to everyone. Which symbol does the build print next to /cart?

        Starter codeTSX
        export default function CartPage() {
          return (
            <main>
              <h1>Your cart</h1>
              <p>Your cart is empty.</p>
            </main>
          );
        }
        

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeFind the missing line

          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?

          Starter codeTSX
          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>
            );
          }
          

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeChoose for a tea blog

            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?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, think about where the code runs, and when its HTML is made.

              The Why Next.js? quiz · 8 questionsScore: first tries count
              1. 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.

              2. 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.

              3. Question 3 of 8Which file is the page for the address /cart in a Next.js app?

                Choose an answer to see the explanation.

              4. Question 4 of 8The tea shop's build prints ○ / and ◐ /cart. Why is /cart different?

                Choose an answer to see the explanation.

              5. Question 5 of 8In the tea shop, which component's code is sent to the browser?

                Choose an answer to see the explanation.

              6. Question 6 of 8This file is app/about/page.tsx in the tea shop. What does next build print for it?

                Read the code, then predictTSX
                export 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.

              7. Question 7 of 8What happened to Remix's React features?

                Choose an answer to see the explanation.

              8. 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 app are addresses, page.tsx makes 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 build prints 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.

              Read it in Next.js's own code

              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.

              CompleteFrontend Clear concepts. Working examples.