Home/Learn/Next.js/The App Router — Files Are the Routing

The App Router — Files Are the Routing

Beginner
Foundations

A folder is a URL segment and a handful of reserved filenames give you the page, its layout, its loading state and its error boundary. There is no route configuration to write.

Overview

Next.js has no router configuration file. The shape of the `app` directory is the routing table: each folder becomes a URL segment, and reserved filenames inside it declare what that segment renders. This is quick to learn and has a real payoff — a route's page, layout, loading UI, error boundary and data all sit in one folder, so the whole feature is one place. The cost is that the conventions are load-bearing: a file named wrongly does nothing, silently.

Folders and Reserved Files

The full set, and what each one does.

The reserved filenames
app/
  layout.tsx            // root layout — REQUIRED, wraps everything
  page.tsx              // /
  loading.tsx           // Suspense fallback for this segment
  error.tsx             // error boundary ('use client', required)
  not-found.tsx         // 404 for this segment
  global-error.tsx      // catches errors in the root layout itself

  problems/
    page.tsx            // /problems
    loading.tsx         // shown while /problems streams
    [slug]/
      page.tsx          // /problems/two-sum
      opengraph-image.tsx

  api/
    health/route.ts     // GET /api/health — a handler, not a page

// Only page.tsx and route.ts make a segment publicly routable. A
// folder of components with no page.tsx is not a URL — which is why
// colocating components inside a route folder is safe.

// Dynamic segments
[slug]        // /problems/two-sum   -> params.slug
[...path]     // catch-all           -> params.path is an array
[[...path]]   // optional catch-all  -> also matches the parent

A Page and Its Params

The two props every page receives, and generating static paths.

params, searchParams, generateStaticParams
// app/problems/[slug]/page.tsx
export default async function ProblemPage({ params, searchParams }) {
  const problem = await getProblem(params.slug)      // /problems/two-sum
  const tab = searchParams.tab ?? 'description'      // ?tab=solution
  ...
}

// Reading searchParams makes the route DYNAMIC — it cannot be known
// at build time. If you only need it in one small part of the page,
// read it in a client component with useSearchParams instead, and
// keep the rest static.

// Pre-render the known paths at build time
export async function generateStaticParams() {
  const problems = await listProblemSlugs()
  return problems.map((slug) => ({ slug }))
}
// Everything returned here becomes a static page. Anything not
// listed is rendered on demand and then cached, unless you forbid it:
export const dynamicParams = false                  // 404 instead

// In Next 15+, params and searchParams are Promises and must be
// awaited — the single most common upgrade error:
const { slug } = await params

Groups, Private Folders and Parallel Routes

Organising without changing the URL.

(group), _private, @slot, (.)intercept
// Route groups: (name) organises without adding a URL segment
app/
  (marketing)/
    layout.tsx          // a marketing shell
    page.tsx            // /
    pricing/page.tsx    // /pricing        <- not /marketing/pricing
  (app)/
    layout.tsx          // a signed-in shell — a DIFFERENT root layout
    dashboard/page.tsx  // /dashboard

// Private folders: _name is never routable, for colocated code
app/problems/_components/ProblemCard.tsx

// Parallel routes: render two independent subtrees in one layout
app/
  @sidebar/page.tsx
  @main/page.tsx
  layout.tsx            // receives { sidebar, main } as props

// Intercepting routes: show a route as a modal over the current page,
// but as a full page on a direct visit or a refresh — the Instagram
// photo-grid behaviour
app/problems/(.)preview/[slug]/page.tsx

// Parallel and intercepting routes are powerful and easy to overuse.
// Reach for them when you have the specific problem; not before.

Key Points to Remember

  • 1Folders are URL segments and reserved filenames declare what each segment renders — there is no route config
  • 2Only page.tsx or route.ts makes a segment routable, so components can safely be colocated inside route folders
  • 3Reading searchParams forces a route to be dynamic; useSearchParams in a small client component keeps the rest static
  • 4generateStaticParams pre-renders known dynamic paths at build time
  • 5Route groups organise without changing the URL, and _folders are never routable

Interview Questions

Sign in to ask Aria
1

How does file-based routing work in the App Router?

Easy
2

What is a route group and when would you use one?

Medium
3

What does generateStaticParams do?

Medium

Ask Aria about The App Router — Files Are the Routing

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…