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

Project structure

Find your way around a Next.js project: the app folder and its special files, public, src, next.config.ts, and where your own files can go.

By the end, you can
  • 01
    Read a project's foldersSay what app, public and the top-level files are for, and which files in app become addresses.
  • 02
    Recognize the special filesName what layout, page, loading, error, not-found and route each make, ahead of the lessons that teach them.
  • 03
    Put files in the right placeChoose between src and the top level, and keep your own components and data next to a page or beside app.

A map of the tea shop's project

Creating a Next.js app left you with a folder full of files. This lesson is the map: what each folder and file is for, which of them become addresses on the site, and where your own files can go. Here is the tea shop's project, with two pages and a menu file:

The tea shop's projectText
tea-shop/
  app/
    teas/
      page.tsx
      tea-list.tsx
      teas.ts
    layout.tsx
    page.tsx
  public/
    menu.txt
  next.config.ts
  package.json
  tsconfig.json

Lines 2 to 8 are the app folder, with the pages. Lines 9 and 10 are the public folder, with one text file. Lines 11 to 13 are settings files, at the project's top level: directly in tea-shop, not inside another folder. That's a real project. It was built with next build, run with next start, and asked for five addresses. Click the files to read them, and the addresses above the page to switch between them.

The tea shop, with two pages and a menu fileNext.js
app/teas/public/
app/teas/page.tsx
import { getTeas } from "./teas";import TeaList from "./tea-list"; export default async function TeasPage() {  const teas = await getTeas();  return (    <main>      <h1>Our teas</h1>      <TeaList teas={teas} />    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/Tea shop
Tea shop

Three teas, brewed fresh.

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

app/teas/page.tsx is the page for /teas. Lines 1 and 2 import two files that sit next to it in the same folder: teas.ts, the shop's data, and tea-list.tsx, a component that draws the list. The Browser tab shows the page at /teas with its three teas. Now pick /teas/tea-list or /teas/teas: both answer 404, the page for an address that doesn't exist. A file in app only becomes an address when it's a page.

The tea shop's project as three groups. A box named app holds layout.tsx, page.tsx and teas/page.tsx: files that become addresses. A box named public holds menu.txt: files sent as they are. Beside them, next.config.ts, package.json and tsconfig.json: settings for the tools.layout.tsxpage.tsxteas/page.tsxappbecome addressesmenu.txtpublicsent as they aretop levelnext.config.tspackage.jsontsconfig.jsonsettings for the tools
Three kinds of things sit at the top of the project. app holds the files that become addresses, public holds files sent exactly as they are, and the files beside them hold the settings.

Each row is one thing at the top level of the project:

What's at the top level
File or folderWhat it's forWho reads it
app/Your pages, layouts and the special files, in folders that become addresses.Next.js, when it builds and serves the app.
public/Files sent exactly as they are, like pictures and a menu.The server, which answers with them by name.
next.config.tsNext.js's settings.Next.js, every time next dev, next build or next start starts.
package.jsonThe packages the project needs, and its scripts.npm.
tsconfig.jsonTypeScript's settings, including the @/* alias.TypeScript, your editor, and Next.js for the alias.
Real-life analogyThe tea shop's building

Customers walk into the rooms that have a door to the street, and each door has a sign. The back room is right behind the counter, but it has no door to the street, so customers never walk into it, even though the tea brewed there is served at the counter. The leaflets by the entrance are there for anyone to take. And the office holds the rules the whole building runs by.

In real life: Rooms with a door to the street, each with its own sign
In Next.js: Folders in app with a page.tsx, each an address
In real life: The back room behind the counter, with the stock and the tools
In Next.js: Files next to a page, like tea-list.tsx: no address
In real life: The rack of free leaflets by the door
In Next.js: public: anyone who asks gets the file, as it is
In real life: The office, with the building's rules
In Next.js: next.config.ts and the other settings files
The main idea

In a Next.js project, app holds the pages and the files that become addresses, public holds files sent as they are, and the files at the top level hold the settings. Inside app, only special file names, like page.tsx, make an address, so your own files can live right next to the pages that use them.

The app folder: folders become addresses

The app folder is the App Router: the part of Next.js that turns folders into addresses. A route is one address the site answers, like /teas. The rules, as a first look:

  • app itself is the address /, so app/page.tsx is the home page.
  • Each folder inside adds a part to the address, called a segment: app/teas is /teas.
  • A segment becomes a page only when its folder has a page.tsx. That file's component is what visitors see.
  • app/layout.tsx is the root layout: it's around every page. In the recording, every response, even the 404, starts with the layout's links, Home and Teas.

Open the example's Terminal tab. After building, Next.js lists the routes it found: ○ /, ○ /_not-found and ○ /teas. /_not-found is the page Next.js adds for addresses that don't exist. tea-list.tsx and teas.ts aren't in the list.

An editor window for the app folder lists page.tsx, teas/page.tsx, teas/tea-list.tsx and teas/teas.ts. Arrows go from page.tsx to the address slash, and from teas/page.tsx to slash teas. tea-list.tsx and teas.ts are crossed out: no address, and asking for one answers 404.apppage.tsxteas/page.tsxteas/tea-list.tsxteas/teas.ts//teasno address: 404
Each folder in app is a part of an address, and a page.tsx makes it a page. The files next to a page have no address: the recording asked for /teas/tea-list and /teas/teas, and both answered 404.

Dynamic addresses like /teas/mint, nested layouts, and the folders that don't change the address all build on these rules. The Routing and navigation stage teaches them one by one.

Special files: a first look

page.tsx and layout.tsx are two of Next.js's special files: names that mean something in app. Each one gives its folder a job. The file can end in .tsx, .jsx or .js (and route in .ts or .js). Each row is one of them, with the lesson that teaches it:

The special files you'll meet first
FileWhat it makesTaught in
layout.tsxThe part around the pages in its folder and below, like the shop's links.Layouts and pages
page.tsxThe page for its folder's address.File-system routing
loading.tsxWhat shows while a page in its folder is still waiting for something.Loading UI
error.tsxWhat shows in place of the page if it throws an error.Error handling
not-found.tsxWhat shows for an address that doesn't exist.Not found, forbidden and unauthorized
route.tsAn address that answers with data, like JSON, instead of a page.Route Handlers

Here is the tea shop with four more of them added to app. The file shown first is app/not-found.tsx:

The tea shop with not-found, error, loading and route filesNext.js
app/api/teas/teas/public/
app/not-found.tsx
import Link from "next/link"; export default function NotFound() {  return (    <main>      <h1>Not on the menu</h1>      <p>        <Link href="/teas">See every tea</Link>      </p>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/oolongTea shop
Teas
Not on the menu

See every tea

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

  • not-found.tsx: the Browser tab opens on /oolong, an address the shop doesn't have. It answered with status 404, and with this file's page: line 6 is the heading Not on the menu, inside the same layout as every page.
  • route.ts: app/api/teas/route.ts makes the address /api/teas, which answers with data instead of a page. Pick it, then open the Network tab: the body is the three teas as JSON, with content-type: application/json. The Terminal tab lists it as a route, ○ /api/teas, next to the pages.
  • error.tsx: shown in place of a page that throws an error. Nothing here throws, so it never shows. Error files must be Client Components, so line 1 is "use client", and its button calls retry to try the page again.
  • loading.tsx: shown while a page waits for something, like data that's made for each request. Every page here is made during the build, so Brewing… never shows either.

The special files in one folder fit inside each other, like boxes. Next.js puts the page inside loading.tsx, that inside error.tsx, and all of it inside the layout:

Three windows, one inside the other: layout.tsx outside, error.tsx inside it, loading.tsx inside that, and page.tsx in the middle.layout.tsxerror.tsxloading.tsxpage.tsx
The special files in one folder fit together: the layout is outside, then error.tsx, then loading.tsx, and the page in the middle. (Simplified: template.tsx and not-found.tsx have their places in this order too.) Later lessons open each one.

Because these names mean something, don't give your own files the same names. A component called page.tsx in a folder would turn that folder into an address. Name it for what it is, like tea-list.tsx.

public: files served as they are

Every file in public gets an address: its path inside public, starting at the top of the site. The word public isn't part of it. So public/menu.txt is /menu.txt, and a picture at public/images/green-tea.svg is /images/green-tea.svg.

In the first example, pick /menu.txt and open the Network tab. The server sent the file exactly as it is, as text/plain, with Cache-Control: public, max-age=0. That header lets browsers keep a copy, but tells them to ask the server again every time before they use it: Next.js can't know whether a file in public has changed. A page uses these files by their address, like <img src="/images/green-tea.svg" />.

Three things to know:

  • Anyone can download anything in public. Never put secrets there.
  • Files in public aren't built: no TypeScript, no bundling. They're sent byte for byte.
  • The site's icon and files for search engines, like favicon.ico and robots.txt, have special files of their own in app. That's why create-next-app put favicon.ico in app, not in public.

The optional src folder

Some teams like their own code apart from the settings files. Next.js lets you move app into a folder named src, short for source. It's the --src-dir answer in create-next-app. The app works the same; only the folder moves. The settings stay at the top level, and so does public. One line of tsconfig.json changes, so the @/ alias points into src:

tsconfig.json with src (the lines around the alias)JSON
    "paths": {
      "@/*": ["./src/*"]
    }

The middle line maps @/* to ./src/*, so import TeaList from "@/app/teas/tea-list" finds src/app/teas/tea-list.tsx. Without src, it says ./*. Try the four layouts below. Each one was built and run with real Next.js. Before you click, guess what / and /menu.txt answer for each.

Playground: where does Next.js look for app and public?
The tea shop's home page, with this layout of foldersNext.js
app/public/
app/page.tsx
export default function Home() {  return (    <main>      <h1>Tea shop</h1>      <p>This page is app/page.tsx.</p>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/Tea shop
Tea shop

This page is app/page.tsx.

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

Try it yourself
app at the top

The usual layout: app and public side by side at the top. / shows the page from app/page.tsx, and /menu.txt is the file in public.

Every version was built with next build from Next.js 16.4.3 and run with next start; the Browser tab shows / and /menu.txt as they were recorded. The tree on the left shows where each file is. Reset goes back to the first version.

So, with src:

  • app moves into src, and so do your own folders, like components and lib.
  • public, next.config.ts, package.json, tsconfig.json and the .env files stay at the top level.
  • Keep only one app. If there's one at the top level too, Next.js uses it and ignores src/app.

next.config.ts and the other top-level files

next.config.ts holds Next.js's settings. Next.js reads it every time next dev, next build or next start starts: a build of the default project prints ✓ Running next.config.ts took 63ms. Every recorded example in this course uses the one create-next-app writes without Tailwind, which turns on Cache Components and Partial Prefetching. Add a setting, and Next.js behaves differently. Here the shop gains one:

next.config.ts with one more settingTypeScript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  cacheComponents: true,
  partialPrefetching: true,
  poweredByHeader: false,
};

export default nextConfig;

Line 3 makes the settings object, and its type, NextConfig, lets your editor suggest every setting. Lines 5 and 6 are the ones create-next-app wrote. Line 7 is new: poweredByHeader: false. Line 10 exports the object for Next.js. Compare the headers with and without it:

Playground: what does one line of next.config.ts change?
The tea shop, with this next.config.tsNext.js
app/teas/public/
next.config.ts
import type { NextConfig } from "next"; const nextConfig: NextConfig = {  /* config options here */  cacheComponents: true,  partialPrefetching: true,}; export default nextConfig;
Recorded from Next.js 16.4.3

GET /200

Response headers
Vary rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch, Accept-Encoding
x-nextjs-cache HIT
x-nextjs-prerender 1
x-nextjs-stale-time 300
X-Powered-By Next.js
Cache-Control s-maxage=31536000
Content-Type text/html; charset=utf-8

The HTML that arrived, one element per line, with the code of its 15 scripts left out

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="preload" as="script" fetchpriority="low" href="/_next/static/chunks/1x6obpmlu1_c2.js">
    <script src="/_next/static/chunks/06bs-nf19b2js.js" async=""></script>
    <script src="/_next/static/chunks/2ki52vdnbo2ft.js" async=""></script>
    <script src="/_next/static/chunks/3l_qmp-61z4yc.js" async=""></script>
    <script src="/_next/static/chunks/turbopack-3-ry682tkyiqk.js" async=""></script>
    <script src="/_next/static/chunks/3oh00mkbkq8u6.js" async=""></script>
    <title>Tea shop</title>
    <script src="/_next/static/chunks/0cz1d0mv5g_q7.js" nomodule=""></script>
  </head>
  <body>
    <div hidden="">
      <!--$-->
      <!--/$-->
    </div>
    <nav><a href="/">Home</a> <a href="/teas">Teas</a></nav>
    <!--&-->
    <main>
      <h1>Tea shop</h1>
      <p>Three teas, brewed fresh.</p>
    </main>
    <!--$-->
    <!--/$-->
    <!--/&-->
    <script id="_R_">…</script>
    <script src="/_next/static/chunks/1x6obpmlu1_c2.js" async=""></script>
    <script>…</script>
    <script>…</script>
    <script>…</script>
    <script>…</script>
    <script>…</script>
    <script>…</script>
    <script>…</script>
  </body>
</html>
Try it yourself
As create-next-app writes it

Cache Components and Partial Prefetching are on. Among the response's headers is X-Powered-By: Next.js, which tells everyone what the site is built with.

Both versions were built and run with Next.js 16.4.3. The Network tab shows the headers of the response for /. Only next.config.ts changes. Reset goes back to the first version.

Most settings are for later lessons: redirects, images, and the bundler. A few other files at the top level have a meaning to Next.js too, and each has its lesson later:

  • .env, .env.local and others: environment variables, settings like secret keys that your code reads, kept out of git.
  • proxy.ts: code that runs before a request reaches a page, to redirect it or check who's signed in. Before Next.js 16 it was called middleware.ts.
  • instrumentation.ts: code that runs once when the server starts, for tools that watch the app.

Where your own files go

Your components, data and helpers aren't special files, and Next.js doesn't mind where they go. Keeping a file next to the code that uses it is called colocation. In app, a file next to a page gets no address: only page.tsx and route.ts make addresses. That's why app/teas/tea-list.tsx answered 404.

Having no address isn't the same as staying on the server, though. What a page renders is sent to the browser in its HTML: the recorded /teas holds every tea from teas.ts. And a file that a Client Component imports is sent to the browser as code, wherever it sits (Server Components explains the two kinds). Secrets, like an API key, belong in code that only runs on the server and in environment variables the browser never gets; the Server-only code and environment variables lesson shows how. Each row is one place your files can go:

Three places for your own files
WhereWhat goes thereDoes it get an address?
Next to the page that uses it, in appA component or data only one page uses, like app/teas/tea-list.tsx.No. Only page.tsx and route.ts make addresses.
In a folder beside appCode many pages share, like components/ and lib/, imported with @/.No. Next.js only makes addresses from app.
In a private folder, like app/_draftsFiles you want to keep out of routing, even a page.tsx.No. The underscore keeps the whole folder out.

Many teams keep shared code beside app, in folders like components and lib. The names mean nothing to Next.js. Here the shop does that, and imports with the @/ alias instead of ../:

app/teas/page.tsx, importing from the top levelTSX
import { getTeas } from "@/lib/teas";
import TeaList from "@/components/tea-list";

export default async function TeasPage() {
  const teas = await getTeas();
  return (
    <main>
      <h1>Our teas</h1>
      <TeaList teas={teas} />
    </main>
  );
}

Line 1 imports getTeas from lib/teas.ts, and line 2 imports TeaList from components/tea-list.tsx. @/ means the project's top level, from tsconfig.json. The recording below proves both halves: /teas shows the teas, and /components/tea-list and /lib/teas answer 404.

The tea shop with components and lib beside appNext.js
app/teas/components/lib/
app/teas/page.tsx
import { getTeas } from "@/lib/teas";import TeaList from "@/components/tea-list"; export default async function TeasPage() {  const teas = await getTeas();  return (    <main>      <h1>Our teas</h1>      <TeaList teas={teas} />    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/teasTea shop
Home
Our teas
  • 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.

One more tool, as a first look: a folder whose name starts with an underscore, like _drafts, is a private folder. Next.js leaves it out of routing completely, even a page.tsx inside it:

A page inside a private folderNext.js
app/_drafts/teas/public/
app/_drafts/page.tsx
export default function Draft() {  return (    <main>      <h1>Autumn menu (draft)</h1>    </main>  );}
Recorded from Next.js 16.4.3
localhost:3000/_drafts404: This page could not be found.
Home Teas
404
This page could not be found.

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

The Terminal tab lists /, /_not-found and /teas, and no /_drafts. Asked for it, the server answered 404. Private folders and route groups get a lesson of their own: Route groups and private folders. Whichever way you choose, use it across the whole project, so everyone knows where to look.

Where does this file go?

The shop keeps growing: an About page, a logo, a menu to download, secret keys. Each card is a file it needs. Sort it into the place it belongs:

Where does this file go?
  • The page for /about.
  • A TeaCard component only the /teas page uses.
  • logo.svg, which every page shows with <img src="/logo.svg">.
  • A menu people download at /menu.pdf.
  • The setting that turns off the X-Powered-By header.
  • .env.local, with the shop's secret keys.
  • The file that sets up the @/* alias.
Try it yourself
0 of 7 correct

Sort each file into app, public, or the project's top level.

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

One way to decide: if visitors open it as a page, it's in app. If they download it as it is, it's in public. If a tool reads it, it's at the top level. The rest is your code, and it goes next to whatever uses it.

Common mix-ups

  • "Every file in app becomes a page." No. Only page.tsx makes a page, and route.ts an address for data. /teas/tea-list answered 404.
  • "Files in public are at /public/…." No. public/menu.txt is at /menu.txt.
  • "With src, everything moves into it." No. public and the settings files stay at the top level. Moved, public stops working.
  • "Next.js warns if there are two app folders." No. It quietly uses the top-level one and ignores src/app.
  • "A file with no address never reaches the browser." No. What a page renders is in its HTML, and a file a Client Component imports is sent as code. Keep secrets in server-only code and environment variables.
  • "Components must go in a components folder." No. The name means nothing to Next.js. Next to the page, or beside app, both work.
  • "next.config.ts is where pages are listed." No. Pages come from the folders in app. next.config.ts only holds settings.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upFind the addresses in a folder

Which addresses does next build list for this app folder, besides /_not-found?

Starter codeText
app/
  about/
    page.tsx
  lib/
    format.ts
  teas/
    page.tsx
    price.ts
  layout.tsx
  page.tsx

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

    Exercise 2 · Warm-upThe address of a picture

    A picture is saved at public/images/green-tea.svg. What address does a page put in its src?

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

      Exercise 3 · PracticeAsk for a component's address

      This is app/teas/page.tsx, next to app/teas/tea-list.tsx. What does the server answer for /teas/tea-list?

      Starter codeTSX
      import { getTeas } from "./teas";
      import TeaList from "./tea-list";
      
      export default async function TeasPage() {
        const teas = await getTeas();
        return (
          <main>
            <h1>Our teas</h1>
            <TeaList teas={teas} />
          </main>
        );
      }
      

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

        Exercise 4 · PracticePoint the alias into src

        You moved app into src. Rewrite the alias line in tsconfig.json.

        Starter codeJSON
        "paths": {
          "@/*": ["./*"]
        }

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

          Exercise 5 · ChallengeTwo app folders

          A teammate added src/app but forgot to delete app. Which file does / show?

          Starter codeText
          app/
            layout.tsx
            page.tsx
          src/
            app/
              layout.tsx
              page.tsx

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

            Exercise 6 · ChallengeHide what the site is built with

            Every response has the header X-Powered-By: Next.js. Which line in next.config.ts removes it?

            Starter codeTypeScript
            import type { NextConfig } from "next";
            
            const nextConfig: NextConfig = {
              /* config options here */
              cacheComponents: true,
              partialPrefetching: true,
            };
            
            export default nextConfig;
            

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

              Check your understanding

              For each question, ask which folder a file is in, and whether its name means something to Next.js.

              The Project structure quiz · 7 questionsScore: first tries count
              1. Question 1 of 7Which file makes /teas a page in the tea shop?

                Choose an answer to see the explanation.

              2. Question 2 of 7The tea shop has public/menu.txt. At which address does the server send it?

                Choose an answer to see the explanation.

              3. Question 3 of 7A project has both app/page.tsx and src/app/page.tsx. Which one does / show?

                Choose an answer to see the explanation.

              4. Question 4 of 7You move your code into src. Where does public go?

                Choose an answer to see the explanation.

              5. Question 5 of 7What does this next.config.ts change?

                Read the code, then predictTypeScript
                import type { NextConfig } from "next";
                
                const nextConfig: NextConfig = {
                  /* config options here */
                  cacheComponents: true,
                  partialPrefetching: true,
                  poweredByHeader: false,
                };
                
                export default nextConfig;
                

                Choose an answer to see the explanation.

              6. Question 6 of 7Which special file decides what an address that doesn't exist shows?

                Choose an answer to see the explanation.

              7. Question 7 of 7A page sits at app/_drafts/page.tsx. What does /_drafts answer?

                Choose an answer to see the explanation.

              Key takeaways

              • app holds the pages; public holds files sent as they are; the top level holds the settings files.
              • In app, each folder is a segment of the address, and page.tsx makes it a page. Other files next to it have no address.
              • Special files give a folder a job: layout, page, loading, error, not-found and route. They fit inside each other, with the page in the middle.
              • public/menu.txt is at /menu.txt. Nothing secret goes in public.
              • src is optional: app moves into it; public and the settings stay at the top level. Keep only one app.
              • Your own files can go next to the page that uses them, in folders beside app, or in a private _folder.

              Remember the one-liner.
              Pages in app, files as they are in public, settings at the top; only special names make addresses.

              Coming next: Your first Next.js app. It builds a small notes app from scratch, with two pages, a shared layout, a link, data from the server, and a form that saves.

              CompleteFrontend Clear concepts. Working examples.