Next.js path · The router · Topic 1 of 11

The App Router: folders, layouts and pages

Not started

The App Router turns the folder tree under app/ into a tree of React components. Each folder is a URL segment; inside it, a few specially named files say what that segment renders: page for the content, layout for the frame around it, loading and error for the in-between states. When a request comes in, Next.js picks the folders that match the URL and nests their files, outermost layout first. Seeing that nesting is the key to almost everything else: rendering, streaming and caching all happen per segment.

Why it matters: Every other Next.js feature (streaming, caching, prefetching) is organised by route segment. If you can read the folder tree as a tree of nested components, the rest of the framework stops being magic.

  1. 1Folders are segments

    Each folder under `app/` is one segment of the URL. A folder only becomes a route when it contains a `page` file; `[slug]` makes a dynamic segment and `(group)` organises files without changing the URL.

  2. 2Layouts nest and persist

    A `layout` wraps every page below it. On navigation between sibling pages the shared layouts are kept, with their state, and only the part that changed is rendered.

  3. 3Special files are boundaries

    `loading` wraps the segment in a Suspense boundary, `error` in an error boundary, `not-found` handles missing content. Their position in the tree decides how much of the page they replace.

1

How it works

The explanation, with examples you can run.

From folders to a component tree

app/  layout.tsx            <html><body>…  (the root layout, required)  page.tsx              /  dashboard/    layout.tsx          sidebar around every dashboard page    loading.tsx         shown while a dashboard page is loading    page.tsx            /dashboard    [teamId]/      page.tsx          /dashboard/acme, /dashboard/globex, …  (marketing)/    pricing/page.tsx    /pricing  (the group adds nothing to the URL)

For /dashboard/acme, the rendered tree is roughly:

<RootLayout>  <DashboardLayout>    <Suspense fallback={<DashboardLoading />}>      <TeamPage params={Promise.resolve({ teamId: "acme" })} />    </Suspense>  </DashboardLayout></RootLayout>

loading.tsx is shorthand for that Suspense boundary, and error.tsx adds an error boundary in the same way. Because they sit at a particular level, a slow or failing page replaces only its own segment; the sidebar in the dashboard layout stays on screen.

Dynamic segments arrive as params, which in Next.js 16 is a promise you await (synchronous access was removed). generateStaticParams can list known values so those pages are prerendered at build time.

Layouts stay, pages change

A layout receives children (the segment below it) and wraps it. When the user navigates between two pages under the same layout, Next.js does a client-side transition: it fetches only the segments that differ and keeps the shared layouts mounted, with their client state and scroll positions. This is why a layout cannot read searchParams or the current pathname directly; it doesn't rerender when they change. Read those in the page, or in a client component with useSearchParams and usePathname.

Links rendered with <Link> are prefetched when they enter the viewport, so the data for the next segment is often in the browser before the click. How much is prefetched depends on what the route can prerender, which is the subject of the rendering topics later in this path.

Colocation

Only page and route files are publicly reachable, so components, tests and helpers can live next to the route that uses them. Folders starting with _ are private and never become routes. Route groups (name) let different sections have different root layouts; navigating between two root layouts is a full page load, since there is no shared frame to keep.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.Which files make `/dashboard/settings` a route?
  2. 2.A user moves from `/dashboard/a` to `/dashboard/b`. Both share `app/dashboard/layout.tsx`, which holds a client sidebar with an open menu. What happens to the menu?
  3. 3.Why can a layout not read `searchParams`?

0 of 3 right so far. Get all three to finish this topic.