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

Your first Next.js app

Build a small notes app in Next.js step by step: a root layout, two pages, a link, notes loaded on the server, and a form that saves with a Server Action.

By the end, you can
  • 01
    Build pages inside a layoutWrite a root layout and two pages, and link them, so every page shares the same frame.
  • 02
    Load and save data on the serverRead notes in an async Server Component, and save a new one from a form with a Server Action.
  • 03
    Build and run the appRead what next build and next start print, and tell them apart from next dev.

A notes app, one step at a time

In this lesson you build a small notes app with Next.js. It has two pages: / lists your notes, and /new has a form to write a new one. Both pages share a row of links at the top. The notes are loaded on the server, and the form saves a note on the server too. You build it in five steps, and each step adds one idea:

  1. A root layout and a first page.
  2. A second page, inside the same layout.
  3. Links between the two pages.
  4. Notes loaded on the server.
  5. A form that saves a note.

Then you build the app for real and run it. Each idea gets a first look here, and a stage of the course of its own later; the text links to it. This is the app you'll have at the end:

The finished notes app. Two browser windows share a shaded row of links, All notes and New note, from the layout. The window for slash lists Buy mint tea and Call Asha, read from notes.ts. The window for slash new has a box, Write a note, and a Save button, which sends the note to actions.ts, which saves it in notes.ts./All notes New noteAll notesBuy mint teaCall Ashanotes.tsreadsactions.tssaves/newAll notes New noteNew noteWrite a noteSaveshaded: the layout, on every page
The app at the end of this lesson. The shaded links come from the layout, on both pages. / reads the notes from notes.ts, and the form at /new saves a note through actions.ts.

Every step below is a real Next.js 16.4.3 project. It was built with next build, run with next start, and what it sent back was recorded. To follow along on your computer, make a project with npx create-next-app@latest notes, replace the files in its app folder with the step's files, and run npm run dev. Creating a Next.js app explains the starter's questions and its files.

Step 1 is two files. app/layout.tsx is the root layout: the component around every page. app/page.tsx is the page for /. Click a file in the tree to read it, then look at the result under it.

Step 1: a root layout and a first pageNext.js
app/
app/page.tsx
export default function Home() {  return (    <main>      <h1>All notes</h1>      <p>No notes yet.</p>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/Notes
Notes app
All notes

No notes yet.

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

In app/page.tsx, Home returns the page: a heading on line 4 and a short message on line 5. The file exports it with export default, which is how Next.js finds the page's component. Its place, right inside app, makes it the page for /.

Now open app/layout.tsx. Lines 5 and 6 give every page its <html> and <body>; a Next.js app needs this file, and it must have both tags. Line 7 is a <header> that every page will show. Line 8, {children}, is where the page goes. LayoutProps<"/"> on line 3 is a type that Next.js writes for you when it builds or runs the app: it says the layout gets children. Line 1 exports metadata, and its title becomes the page's <title>: the browser frame shows Notes next to the address.

The Browser tab shows the page: Notes app, then All notes and No notes yet. The Terminal tab shows what next build printed. Its route table lists ○ /, your page, and ○ /_not-found, the page Next.js adds for addresses that don't exist. The legend under the table says what ○ means: prerendered as static content. Next.js made the page's HTML ahead of time and keeps it, to send to every visitor. In this recording, that first copy was made during the build. A stored copy can be made again later, and step 5 shows how.

Real-life analogyA small café

A café has one front and one sign, and several rooms behind them. Guests walk from room to room through doors. The food comes from a pantry that guests never see. To order, a guest fills in a slip and hands it to the kitchen; the staff do the rest, and the guest sees the dish arrive.

In real life: The café's front and its sign, the same whichever room you sit in
In Next.js: The root layout, around every page
In real life: Each room, with its own door
In Next.js: Each page, with its own address
In real life: The pantry, where only the staff go
In Next.js: notes.ts, which only runs on the server
In real life: An order slip you hand to the kitchen
In Next.js: The form, sent to a Server Action
The main idea

A Next.js app is files in the app folder. A layout goes around every page, a page.tsx makes each address, Server Components load the data, and Server Actions save it. next build turns the files into an app, and next start runs it.

A second page in the same layout

Step 2 adds the page for writing a note. Its address is /new, so its file goes in a new folder, app/new, and is named page.tsx. Nothing else changes.

Step 2: a second page, at /newNext.js
app/new/
app/new/page.tsx
export default function NewNotePage() {  return (    <main>      <h1>New note</h1>      <p>The form goes here.</p>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/Notes
Notes app
All notes

No notes yet.

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

app/new/page.tsx has the same shape as the home page. Line 4 is its heading, New note, and line 5 holds the place where the form will go in step 5. The folder new gives the address /new: one folder for each part of the address. (Routing and navigation starts with how that works.)

The Terminal tab now lists three routes: ○ /, ○ /_not-found and ○ /new. In the Browser tab, pick /new above the page. It starts with Notes app, like /, though app/new/page.tsx never says so: the header comes from the layout. Next.js puts each page inside the root layout, where {children} is.

A card for layout.tsx at the top, with arrows to two browser windows, slash and slash new. Each window starts with a shaded Notes app header from the layout. Below it, the slash window shows All notes and No notes yet, from app/page.tsx, and the slash new window shows New note and The form goes here, from app/new/page.tsx.layout.tsx/Notes appAll notesNo notes yet.app/page.tsx/newNotes appNew noteThe form goes here.app/new/page.tsxshaded: from the layout
One layout, two pages. The shaded Notes app header comes from app/layout.tsx, so both pages have it. Under it, where the layout puts {children}, each address shows its own page.

That's what a layout is for: the parts every page shares, written once. Later lessons add layouts inside folders too, around only the pages in that folder.

Links between the pages

The two pages exist, but a visitor can only reach /new by typing its address. Step 3 puts links in the layout, so every page has them.

Step 3: links in the layoutNext.js
app/new/
app/layout.tsx
import Link from "next/link"; export const metadata = { title: "Notes" }; export default function RootLayout({ children }: LayoutProps<"/">) {  return (    <html lang="en">      <body>        <nav>          <Link href="/">All notes</Link> <Link href="/new">New note</Link>        </nav>        {children}      </body>    </html>  );}
Recorded from Next.js 16.4.3
localhost:3000/Notes
All notes

No notes yet.

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

Line 1 imports Link from next/link, a component that comes with Next.js. Line 10 uses it twice: href="/" goes to the list, and href="/new" to the form. The <nav> around them, on lines 9 to 11, takes the place of the old header.

Click New note in the recorded page: the browser frame switches to the recorded /new, and All notes switches back. Then open the Network tab. Each Link arrived as an ordinary link, <a href="/new">New note</a>, so it works even before any JavaScript has run.

The Steps tab shows what happens once the JavaScript runs. The recorder opened / in real Chrome, then clicked New note. Step 1 is one page load, GET /. While the page was on the screen, Next.js's router also asked for /new with fetch requests marked next-router-prefetch: it loaded the linked page's static parts ahead of time. That's called prefetching. Step 2, the click, made no page load and no request at all: the address changed to /new and the page showed The form goes here. from what was already loaded. That's why you use Link instead of a plain <a> for pages of your own app. The Linking and navigating lesson shows both.

Notes from the server

Step 4 gives the list some notes. A real app keeps them in a database. This one keeps them in a file, app/notes.ts, that stands in for one, and the home page reads them.

Step 4: the home page loads its notesNext.js
app/new/
app/page.tsx
import { getNotes } from "./notes"; export default async function Home() {  const notes = await getNotes();  return (    <main>      <h1>All notes</h1>      <ul>        {notes.map((note) => (          <li key={note.id}>{note.text}</li>        ))}      </ul>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/Notes
New note
All notes
  • Buy mint tea
  • Call Asha

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

Open app/notes.ts first. Lines 2 to 5 are the notes: an array of two objects, each with an id and a text. Lines 7 to 9 are getNotes(), which returns them. It's an async function, like a real database call, so the code that uses it stays the same when a database takes its place.

Back in app/page.tsx, line 1 imports getNotes. Line 3 makes Home an async function, so line 4 can wait for the notes with await. Lines 9 to 11 turn each note into a list item with map, using its id as the key.

A React component that is async? That works because Home is a Server Component: a component that runs only on the server, never in the browser, so it can wait for data while it renders. In Next.js, layouts and pages start as Server Components. The client part of an app starts at a file with "use client" at the top: its components, and everything that file imports, go to the browser too. This app has no such file. (Server Components, in the React course, explains them.) There's no state, no Effect and no loading message here.

Open the Network tab: the HTML for / already holds <li>Buy mint tea</li> and <li>Call Asha</li>. The Terminal still says ○ /. During next build, Next.js ran Home, waited for getNotes(), and stored the HTML. The header x-nextjs-cache: HIT says the server answered with that stored copy.

A real database works differently

Here the notes come back at once, from the server's memory, so Next.js could make the page during the build. A real database answers over the network, and its answer changes as people write notes. Connecting a database shows how to read one, and the Rendering and caching stage how Next.js decides what to store.

A form that saves a note

Step 5 makes /new work. It changes three files: notes.ts learns to save a note, a new file, app/actions.ts, holds the function the form calls, and app/new/page.tsx gets the form. Start with the new file.

Step 5: a form that saves with a Server ActionNext.js
app/new/
app/actions.ts
"use server"; import { revalidatePath } from "next/cache";import { redirect } from "next/navigation";import { saveNote } from "./notes"; export async function addNote(formData: FormData) {  const text = String(formData.get("text") ?? "").trim();  if (text) await saveNote(text);  revalidatePath("/");  redirect("/");}
Recorded from Next.js 16.4.3
localhost:3000/Notes
All notes
  • Buy mint tea
  • Call Asha

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

Line 1 of app/actions.ts is "use server". It marks every function this file exports as a Server Function: a function that runs on the server, which the page can still call from the browser. Next.js gives each one an id, and a call becomes a request to the server. A Server Function that a form calls, to change data, is called a Server Action. (Server Functions, in the React course, explains them.)

addNote, on lines 7 to 12, is the action. It gets the form's fields as formData, a FormData object. Line 8 reads the field named text: String(… ?? "") makes sure it's a string even if the field is missing, and trim() removes spaces at both ends. Line 9 saves it with saveNote, but only if there's any text. Line 10, revalidatePath("/"), tells Next.js that the HTML it stored for / is out of date. Line 11, redirect("/"), sends the visitor to the list.

Now open app/new/page.tsx. Line 1 imports addNote, and line 7 hands it to the form: <form action={addNote}>. That's React's way to connect a form to a function (Form actions). Line 8 is the box, named text, which is the name line 8 of the action reads; aria-label gives the box a name for screen readers. Line 9 is the Save button. In app/notes.ts, lines 11 to 13 are saveNote, which adds the note to the array.

The recorder used this form the way a browser does before the page's JavaScript has loaded. Open the Network tab and pick /new: the HTML has <form action="" enctype="multipart/form-data" method="POST">, and inside it a hidden field, $ACTION_ID_ followed by a long code. React wrote that field: it's the id of addNote. Then pick POST /new, press Save: the recorder typed Water the plants and sent the form. The answer is 303, with location: / and no page at all. Last, pick /, after saving: three notes, and x-nextjs-cache: MISS, which says Next.js made this HTML again instead of using the stored copy.

A browser window for slash new holds the text Water the plants and a Save button. An arrow labeled POST goes to a shaded card, addNote, which has an arrow labeled saves to a card, notes.ts. From addNote, an arrow labeled 303, location slash goes down to a window for slash that lists Buy mint tea, Call Asha and Water the plants, labeled made again: MISS./newWater the plantsSavePOSTaddNotesavesnotes.ts303, location: //Buy mint teaCall AshaWater the plantsmade again: MISS
Pressing Save. The browser posts the form to /new, and Next.js calls addNote, which saves the note and marks / as out of date. The 303 answer sends the browser to /, which Next.js makes again with three notes.

Step through it below. Before you start, guess: after which step does the visitor see the new note?

Playground: what happens when you press Save
Who does whatPress Save
What the visitor seesafter step 1
localhost:3000

All notes New note

New note

Write a note Save

React isn't running yet: clicks do nothing.

The page isn't complete yet.

Step 1 of 7
Browser: The visitor types a note and presses Save

The page at /new shows the form. The visitor types Water the plants into the box and presses Save.

Each step is one thing that happens, in order, as the recording shows it: the form submitted before the page's JavaScript has loaded. Every screen is a page the recorder got back. Reset goes back to the first step.

The note shows after step 7, the last one. Everything before it happened on the server, or on the way there and back. Forms that call Server Actions work before the page's JavaScript has loaded, which is why the recorder could save a note with none at all.

Most visitors press Save after the JavaScript has loaded, and then the trip is different. The recorder did that too, in real Chrome: it opened /new, typed Water the plants, and pressed Save. Open the Steps tab and pick the last step.

Step 5 again, with JavaScript runningNext.js
app/new/
app/new/page.tsx
import { addNote } from "../actions"; export default function NewNotePage() {  return (    <main>      <h1>New note</h1>      <form action={addNote}>        <input name="text" aria-label="Note" placeholder="Write a note" />        <button type="submit">Save</button>      </form>    </main>  );}
Recorded from Next.js 16.4.3

Each step was taken in real Chrome against next start. Pick one to see the page after it.

localhost:3000/Notes
All notes New note
All notes
  • Buy mint tea
  • Call Asha
  • Water the plants
Focus
nothing: the page itself
Scrolled
0 px down
Announced to screen readers
All notes

Requests the page made during this step (1), without the files it loaded

  1. POST/new200text/x-component
    next-router-state-tree
    %5B%22%22%2C%7B%22children%22%3A%5B%22new%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%2C514%5D%7D%2Cnull%2Cnull%2C514%5D%7D%2Cnull%2Cnull%2C530%5D
    next-action
    40f464c21bee605d99e71bab019b8a06b0082be0f4

Pressing Save made one request, and no page load: a fetch that posted the form to /new with a next-action header, the action's id. It answered 200 with text/x-component, not HTML. That answer is an RSC payload: data that describes what the Server Components rendered, for React to put on the screen. With it, the address changed to / and the list showed three notes, without the browser loading a new page. The docs describe it this way: an action returns the updated UI and data in one round trip. The Fetching and mutating data stage looks inside that answer.

What does line 10 really do? Try the app without it. Each button below is one version of app/actions.ts, built and run the same way. Before you click, guess what / lists after Save without revalidatePath.

Playground: does the list change after Save?
The notes app, with this version of app/actions.tsNext.js
app/new/
app/actions.ts
"use server"; import { revalidatePath } from "next/cache";import { redirect } from "next/navigation";import { saveNote } from "./notes"; export async function addNote(formData: FormData) {  const text = String(formData.get("text") ?? "").trim();  if (text) await saveNote(text);  revalidatePath("/");  redirect("/");}
Recorded from Next.js 16.4.3
localhost:3000/Notes
All notes
  • Buy mint tea
  • Call Asha
  • Water the plants

The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.

Try it yourself
With revalidatePath

Line 10 tells Next.js that the stored HTML of / is out of date. After the redirect, Next.js makes / again (x-nextjs-cache: MISS), and the list shows Water the plants.

Both versions were built with next build from Next.js 16.4.3 and run with next start. The recorder asked for /, submitted the form at /new with Water the plants, then asked for / again. Only app/actions.ts changes. Reset goes back to the first version.

Without revalidatePath, the note is saved, but / still shows two notes, with x-nextjs-cache: HIT. Next.js made the HTML of / during the build, and keeps sending that copy until something says it's out of date. Saving data and showing it again are two separate jobs. Updating the UI after a mutation covers each way to show fresh data after a change.

Anyone can call an action

A Server Action is a request to your server, and anyone can send one, not only your form. Next.js checks that the request comes from a page on the same site (the browser's origin header), but who the visitor is, and whether they may save, is up to your code. A real app checks that inside every action. The Building real apps stage covers sign-in and security.

Building and running it for real

While you write code, npm run dev runs next dev: a server that makes each page again whenever you ask for it, so every change you save shows at once. Visitors get something else: the app that next build makes, run by next start. The package.json in the file tree has a script for each: dev, build and start. Each row is one command:

The three commands
CommandWhat it doesWhen you use it
next devStarts a server for writing code. It makes each page again when you ask for it, so you see every change you save.While you build the app, on your own computer.
next buildBundles the code, checks the types, and makes the HTML of every page it can make ahead of time. It prints the route table.Once, before the app goes live.
next startRuns the app that next build made, and answers requests with it. It doesn't build anything.On the server that visitors reach, after next build.

Here is the finished app once more, with its Terminal tab open. It shows both commands, as the recorder ran them.

The finished notes app, built and startedNext.js
app/new/
app/actions.ts
"use server"; import { revalidatePath } from "next/cache";import { redirect } from "next/navigation";import { saveNote } from "./notes"; export async function addNote(formData: FormData) {  const text = String(formData.get("text") ?? "").trim();  if (text) await saveNote(text);  revalidatePath("/");  redirect("/");}
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└ ○ /new ○  (Static)  prerendered as static content $ next start▲ Next.js 16.4.3- Local:         http://127.0.0.1:3000- Network:       http://127.0.0.1:3000✓ Ready

Read the marked lines of next build from the top:

  • ✓ Compiled successfully: Turbopack, Next.js's bundler, turned your files into the JavaScript the server and the browser run.
  • Finished TypeScript: the build checked your types. A type error stops the build here, before anything goes live.
  • ✓ Generating static pages (4/4): Next.js ran the pages it could make ahead of time and stored their HTML.
  • The route table: ○ /, ○ /_not-found and ○ /new. The form and the action didn't change any symbol: /new is still prerendered as static content, and the action runs only when someone sends the form.

Under $ next start, the server printed its address and ✓ Ready: from then on, it answers requests with what the build made. (The recorder asked it to listen only on this computer, 127.0.0.1; started on its own, it prints localhost.) next start builds nothing, so after every change you run next build again.

Putting the app online means running those two commands on a computer that visitors can reach, or giving the project to a hosting platform that runs them for you. One thing changes when you do: these notes live in the server's memory. They're gone when the server restarts, and two copies of the server would each have their own list. That's why real apps keep their data in a database. Deploying Next.js covers the ways to deploy, from your own Node.js server to a static export.

Which file does which job

Every Next.js app, however big, is made of the same kinds of files you wrote here. Each row is one step: the files it added, and what its recording shows.

The five steps
StepFiles you wroteWhat the recording shows
1. A root layout and a pageapp/layout.tsx and app/page.tsx○ /: one page, under the layout's Notes app header.
2. A second pageapp/new/page.tsx○ /new: a second page, with the same header.
3. Linksapp/layout.tsx again<a href="/"> and <a href="/new"> in the HTML of both pages.
4. Data from the serverapp/notes.ts, and app/page.tsx againBuy mint tea and Call Asha in the HTML of /.
5. A form that savesapp/actions.ts, and app/new/page.tsx and app/notes.ts againPOST /new answered 303, and / then lists three notes.

A bigger app has more of each: more pages, layouts inside folders, a real database behind something like notes.ts, and an action for every change. Sort these jobs: which file in the notes app does each one?

Which file does this job?
  • Gives every page its <html> and <body>.
  • Shows the All notes and New note links on every page.
  • Gives the browser tab the title Notes.
  • Shows the list of notes at /.
  • Shows the box and the Save button at /new.
  • Keeps the notes and adds a new one.
  • Reads the text from the form's fields.
  • Tells Next.js the list at / is out of date.
Try it yourself
0 of 8 correct

Sort each job into the file of the notes app that does it.

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

Common mix-ups

  • "The layout is part of the home page." No. It's around every page: the recorded /new has the same header and links as /.
  • "A page needs an Effect to load its data." No. A page starts as a Server Component, so it can be async and wait for the data while it renders. The notes were in the first HTML.
  • "A form needs an API and a fetch call." No. <form action={addNote}> calls the Server Action. Next.js makes the request, even before the page's JavaScript has loaded.
  • "Saving the note updates the list." No. Without revalidatePath("/"), Next.js keeps sending the HTML of / it made during the build.
  • "Only my form can call my action." No. An action is a request to your server, and anyone can send one. Check who's asking inside the action.
  • "What next dev shows is what visitors get." No. next dev makes pages again on every request, for writing code. Visitors get what next build made, run by next start.
  • "The notes are saved for good." No. They live in the server's memory. A new next start begins again with the two notes in the file.

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-upFind the notes in the first HTML

This is app/page.tsx in step 4. Which notes are already in the HTML of / when it arrives?

Starter codeTSX
import { getNotes } from "./notes";

export default async function Home() {
  const notes = await getNotes();
  return (
    <main>
      <h1>All notes</h1>
      <ul>
        {notes.map((note) => (
          <li key={note.id}>{note.text}</li>
        ))}
      </ul>
    </main>
  );
}

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

    Exercise 2 · Warm-upFind where the title comes from

    These are the app's files in step 4. Both pages show Notes in the browser tab, but neither page file says so. Which file gives them that title?

    Starter codeText
    app/
      new/
        page.tsx
      layout.tsx
      notes.ts
      page.tsx

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

      Exercise 3 · PracticeCount the notes

      Under the heading, show how many notes there are. What does the recorded page show on that line?

      Starter codeTSX
      // app/page.tsx
      import { getNotes } from "./notes";
      
      export default async function Home() {
        const notes = await getNotes();
        return (
          <main>
            <h1>All notes</h1>
            {/* Show how many notes there are here, like "2 notes" */}
            <ul>
              {notes.map((note) => (
                <li key={note.id}>{note.text}</li>
              ))}
            </ul>
          </main>
        );
      }

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

        Exercise 4 · PracticeFind why the list doesn't change

        With this app/actions.ts, pressing Save saves the note and goes back to /, but the list doesn't show it. Which line is missing?

        Starter codeTypeScript
        "use server";
        
        import { redirect } from "next/navigation";
        import { saveNote } from "./notes";
        
        export async function addNote(formData: FormData) {
          const text = String(formData.get("text") ?? "").trim();
          if (text) await saveNote(text);
          redirect("/");
        }
        

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

          Exercise 5 · ChallengeFind why the build stops

          With this app/actions.ts, next build stops while it makes /new: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Which line is missing?

          Starter codeTypeScript
          import { revalidatePath } from "next/cache";
          import { redirect } from "next/navigation";
          import { saveNote } from "./notes";
          
          export async function addNote(formData: FormData) {
            const text = String(formData.get("text") ?? "").trim();
            if (text) await saveNote(text);
            revalidatePath("/");
            redirect("/");
          }
          

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

            Exercise 6 · ChallengePress Save with an empty box

            A visitor presses Save without typing anything. The server answers 303 and the browser goes to /. What does the list show?

            Starter codeTypeScript
            "use server";
            
            import { revalidatePath } from "next/cache";
            import { redirect } from "next/navigation";
            import { saveNote } from "./notes";
            
            export async function addNote(formData: FormData) {
              const text = String(formData.get("text") ?? "").trim();
              if (text) await saveNote(text);
              revalidatePath("/");
              redirect("/");
            }
            

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

              Check your understanding

              For each question, think about which file does the job, and when it runs: during the build, or when a request comes in.

              The first Next.js app quiz · 7 questionsScore: first tries count
              1. Question 1 of 7Which file gives every page of the notes app its <html> and <body>?

                Choose an answer to see the explanation.

              2. Question 2 of 7In step 4, when did Home run to make the HTML of / that the recorder got?

                Read the code, then predictTSX
                import { getNotes } from "./notes";
                
                export default async function Home() {
                  const notes = await getNotes();
                  return (
                    <main>
                      <h1>All notes</h1>
                      <ul>
                        {notes.map((note) => (
                          <li key={note.id}>{note.text}</li>
                        ))}
                      </ul>
                    </main>
                  );
                }
                

                Choose an answer to see the explanation.

              3. Question 3 of 7Before the page's JavaScript has loaded, what does pressing Save send?

                Read the code, then predictTSX
                import { addNote } from "../actions";
                
                export default function NewNotePage() {
                  return (
                    <main>
                      <h1>New note</h1>
                      <form action={addNote}>
                        <input name="text" aria-label="Note" placeholder="Write a note" />
                        <button type="submit">Save</button>
                      </form>
                    </main>
                  );
                }
                

                Choose an answer to see the explanation.

              4. Question 4 of 7This is app/actions.ts without revalidatePath. After saving Water the plants, what does / list?

                Read the code, then predictTypeScript
                "use server";
                
                import { redirect } from "next/navigation";
                import { saveNote } from "./notes";
                
                export async function addNote(formData: FormData) {
                  const text = String(formData.get("text") ?? "").trim();
                  if (text) await saveNote(text);
                  redirect("/");
                }
                

                Choose an answer to see the explanation.

              5. Question 5 of 7What did the server answer to the submitted form, in the recording?

                Choose an answer to see the explanation.

              6. Question 6 of 7What does next start do?

                Choose an answer to see the explanation.

              7. Question 7 of 7Where do the saved notes live in this app?

                Choose an answer to see the explanation.

              Key takeaways

              • app/layout.tsx is the root layout: the <html>, the <body> and whatever every page shares. Each page goes where {children} is.
              • A page.tsx makes an address: app/page.tsx is /, and app/new/page.tsx is /new.
              • Link from next/link links your own pages. It arrives as a plain <a>, and Next.js makes the move faster once the page's JavaScript runs.
              • Pages start as Server Components, so they can be async and wait for their data. The notes were in the HTML, made during the build.
              • A "use server" file holds Server Actions. <form action={addNote}> calls one, even before JavaScript loads; revalidatePath makes the changed page again, and redirect moves the visitor on.
              • next build makes the app and prints the route table; next start runs it. next dev is only for writing code.

              Remember the one-liner.
              Pages and a layout in app make the app, Server Components read the data, Server Actions save it, and next build then next start run it.

              Coming next: File-system routing. It opens the Routing stage with the rules you used in step 2: how folders become parts of an address, and which files never become pages.

              CompleteFrontend Clear concepts. Working examples.