Module F-2·27 min read

Every special file in the app/ directory — page, layout, template, loading, error, not-found, default, forbidden, unauthorized — and exactly what each one does and when to reach for it.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

F-2 — The App Router: File-System Routing & The Special File Matrix

Who this is for: Developers who completed F-1 and understand the rendering spectrum conceptually, but haven't yet written App Router code. This module is the practical foundation: by the end you'll understand exactly what every file in your app/ directory does, why the naming convention matters, and how the router composes nested layouts without you writing a single line of router configuration.


Why File-System Routing Exists

Every React application that grew past a single page has eventually arrived at the same moment: someone had to choose a router, read its documentation, decide on a pattern for nesting routes, figure out where to put protected routes, and then explain the architecture to every new engineer who joined the team.

React Router, TanStack Router, Wouter — they're all excellent libraries. They also all require you to make a set of decisions that turn out to be the same decisions every team makes, slightly differently, and then documents inconsistently.

File-system routing is the answer to that observation. The structure of your codebase is the documentation. When you open app/dashboard/settings/profile/page.tsx, you already know the URL without reading any router config. When you open app/dashboard/layout.tsx, you already know it wraps every page inside /dashboard. The conventions are load-bearing — they eliminate a category of architectural decision that was never worth making team-by-team.

The App Router takes this further than any previous React routing system. It's not just about mapping files to URLs. It's about collocating every concern — rendering behavior, data fetching, loading states, error boundaries, metadata — with the route segment it belongs to.


The Mental Model: Route Segments

Before touching any file names, get this model locked in: the app/ directory is a tree of route segments, and each folder in that tree is one segment.

text

Folders define segments. Files define what happens at each segment. The page.tsx file is what makes a segment publicly accessible — a folder with no page.tsx is still a valid segment for layout and organisational purposes, but it renders nothing at that URL.

This distinction matters. You can have a folder that organises your files without creating a URL:

text

That (marketing) folder with parentheses is a route group — it exists for your filesystem organisation but contributes nothing to the URL. You'll use these constantly for large applications to keep related pages together without polluting URL structure.


The Special File Matrix

Next.js gives the app/ directory a fixed vocabulary of file names. Each name does exactly one thing. There are eleven of them, and understanding all eleven is understanding the App Router:

FilePurpose
page.tsxMakes the segment publicly accessible at its URL
layout.tsxPersistent wrapper — survives navigation within the segment
template.tsxRe-mounting wrapper — reinstantiates on every navigation
loading.tsxSuspense boundary — shown while the segment's async content loads
error.tsxError boundary — shown when anything in the segment throws
not-found.tsx404 boundary — shown when notFound() is called or no route matches
forbidden.tsx403 boundary — shown when forbidden() is called (Next.js 15)
unauthorized.tsx401 boundary — shown when unauthorized() is called (Next.js 15)
default.tsxParallel route fallback — shown when no slot has a matching segment
route.tsAPI endpoint — handles HTTP verbs, no UI rendered
middleware.tsEdge function — runs before every matched request

forbidden.tsx/unauthorized.tsx work exactly like not-found.tsx — a boundary component that renders when the matching function (forbidden()/unauthorized(), both from next/navigation) is called anywhere in the segment. They're the two auth-specific counterparts to notFound(), and Module 13 covers them in full alongside Auth.js session handling.

Learn these eleven names. They're the entire API surface for controlling what happens at each segment of your application. Everything else in Next.js is built on top of these.


page.tsx — The Public Face

A page.tsx (or .jsx, .js — TypeScript .tsx is the standard) is a React component that becomes the content for its URL. It's a Server Component by default:

tsx

Pages receive two props that Next.js injects automatically:

tsx

The params and searchParams props are Promises in Next.js 15 and later — this is a React 19 change. If you see code that destructures them synchronously without await, that's Next.js 14 code. It still works (Next.js 15 supports both patterns during the transition), but new code should await them.

params contains the dynamic segment values for that route. searchParams contains the query string at the time of the request — but only for dynamic pages; statically generated pages receive an empty object.


layout.tsx — The Persistent Shell

A layout wraps all pages and nested layouts in its segment. It persists across navigations within that segment — its component state is preserved, it doesn't unmount and remount when a user navigates from /dashboard to /dashboard/settings.

tsx

The children prop is where the matched child page (or nested layout) gets inserted. You never import children or specify what it is — Next.js handles the composition automatically.

Layouts nest. If you have app/layout.tsx, app/dashboard/layout.tsx, and app/dashboard/settings/layout.tsx, all three wrap the settings page in order — the root layout outermost, the settings layout innermost, with the page inside it.

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.