The App Router turns a folder structure into a URL structure — folders map to route segments, and a small set of special files (page, layout, loading, error) define what renders for each one. Every layout and page is a Server Component by default, which is the single fact that shapes everything else in this domain.
1. What determines a Next.js App Router application's URL structure?
Interview Questions
2 questions
Cheatsheet
Download-ready reference
Cheatsheet
App Router: folder structure inside app/ = URL structure. No central
route config file.
page.tsx → makes a segment REACHABLE, provides its unique UI.
layout.tsx → shared UI wrapping page.tsx + nested routes via {children}.
PRESERVES STATE, stays interactive, does NOT re-render on navigation
between the pages it wraps.
Root layout (app/layout.tsx): REQUIRED. Must contain <html> and <body>.
The only layout guaranteed to wrap every single route.
Every layout/page = a SERVER COMPONENT by default.
Can `await` data directly. Zero JS shipped unless "use client".
Layouts nest exactly like folders nest:
app/blog/layout.tsx wraps app/blog/page.tsx AND app/blog/[slug]/page.tsx
PageProps<'/route'> / LayoutProps<'/route'> — auto-generated global
helper types for params/searchParams/slots. No import needed.
Special files (each its own topic):
loading.tsx → auto Suspense fallback
error.tsx → auto error boundary
not-found.tsx → 404 UI
route.ts → custom request handler instead of a page
[slug] / [...slug] / [[...slug]] → dynamic segments
@slot, (..) → parallel & intercepting routes
The beginner framing: the App Router is Next.js's routing system where the folder structure inside app/ directly determines your application's URL structure — no separate router configuration file to maintain.
The precise mental model: two special files do almost all the work. A page.tsx file makes a route segment publicly reachable and renders its unique UI. A layout.tsx file wraps shared UI around one or more pages, and — critically — layouts preserve state, remain interactive, and never re-render on navigation between the pages they wrap.
app/ layout.tsx ← root layout, REQUIRED, wraps everything page.tsx ← the "/" route blog/ layout.tsx ← wraps every route under /blog page.tsx ← the "/blog" route [slug]/ page.tsx ← the "/blog/:slug" route
// app/layout.tsx — the root layout is required and must contain html/bodyexport default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body>{children}</body> </html> );}// app/blog/page.tsx — a page, rendered where {children} appears aboveexport
Layouts nest exactly the way folders nest: navigating from /blog to /blog/hello-world re-renders only app/blog/[slug]/page.tsx — the root layout and the blog layout both stay mounted, untouched.
Every page.tsx and layout.tsx is a React Server Component unless you explicitly opt out. This means, by default, every route in your app can await data directly in its component body, never ships its own JavaScript to the browser, and can safely touch secrets and database credentials — the same model covered in Server Components. Interactivity (state, event handlers, browser APIs) requires explicitly marking a component "use client" — covered in depth in Server/Client Component Composition.
The rest of the special-file vocabulary (previewed here, each covered in depth later)#
A user is on /dashboard (with some client-side state inside Dashboard Layout, like an open sidebar) and clicks a link to /dashboard/settings. Does the sidebar's open/closed state survive?
Solution
Yes. Only app/dashboard/page.tsx is being swapped out for app/dashboard/settings/page.tsx — both Root Layout and Dashboard Layout wrap both routes and are never unmounted during this navigation. Any client-side state living in Dashboard Layout (or anything above it) survives completely untouched, exactly as the "layouts preserve state, remain interactive" guarantee describes.
This mirrors the real mechanism closely: matching is a recursive walk down the folder tree by URL segment, and rendering wraps outward-in — the deepest matched page renders first, then each ancestor's layout wraps that result moving back up to the root.
Every layout/page being a Server Component by default is a direct application of Server Components's core distinction — the App Router's file conventions are really just a routing-and-composition layer built on top of that React primitive. And a layout "not re-rendering" on navigation is the same nested-component-tree idea from Components: Layout renders {children} without knowing or caring what's inside — swapping the page underneath it is exactly like swapping a prop, not touching Layout itself at all.
Every other layout in the tree is a fragment of shared UI; the root layout specifically must own the actual document shell, since it's the only layout guaranteed to wrap every single route in the app.
2. Adding a page.tsx where only a layout.tsx was intended#
A folder with a layout.tsx but no page.tsx is not itself a reachable route — it's purely a wrapper for its children. Forgetting this can lead to confusion about why a folder "doesn't have a page" even though it visually groups related routes.
3. Expecting a layout to receive params the same way a page does without asking for them#
Layouts can receive params for their own segment (typed via LayoutProps<'/route'>), but a common mistake is expecting a layout higher up the tree to automatically know about a child segment's dynamic params — it doesn't, unless that segment is the layout's own.
Keep the root layout minimal — the document shell, global providers, and anything truly global. Push feature-specific UI down into nested layouts.
Default to Server Components for every route, opting into "use client" only for the specific interactive pieces (see Server/Client Component Composition).
Use nested layouts to share UI, not by repeating markup across every page.tsx — a shared sidebar or nav belongs in a layout, not copy-pasted.
Reach for the PageProps/LayoutProps global helper types (auto-generated by next dev/next build) to type params/ correctly instead of hand-writing them.
A layout that never re-renders on navigation between its child pages means its own data-fetching (if any) only happens once per visit to that subtree — colocate genuinely page-specific data fetching in the page itself, not a shared layout, to avoid needlessly re-fetching layout data on every request.
Because most routes are Server Components by default, the App Router's baseline is already "ship less JavaScript" — the discipline covered in Server/Client Component Composition is about not giving that advantage away accidentally.
default
async
function
BlogPage
()
{
const posts = await getPosts(); // Server Component — can await data directly