6. App Router (Modern Next.js)
App Router Overview
The App Router introduced in Next.js 13+ replaces the traditional Pages Router with a React Server Components‑first architecture. All UI lives inside the app/ directory, where each folder corresponds to a route segment and special files define layout, loading, error, and other behaviors.
6.1 App Directory Structure & Core Files
- app Folder – Root directory for all routes. Nested folders create route segments (e.g.,
app/dashboard/settings/page.tsxmaps to/dashboard/settings). - page.tsx – Exports a React component that serves as the route’s UI. By default it is a Server Component.
export default function Page() { returnHello
; } - layout.tsx – Persistent UI shared across child routes. Receives a
childrenprop and does not re‑render on navigation.export default function RootLayout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> ); } - template.tsx – Similar to layout but creates a new instance on each navigation, useful for animations or resetting state.
export default function Template({ children }) { return <div>{children}</div>; } - loading.tsx – Instant loading UI shown via React Suspense while the route’s Server Component streams.
export default function Loading() { return <Spinner />; } - error.tsx – Error boundary for the segment. Receives
errorandresetprops.export default function Error({ error, reset }) { return ( <div> <p>Something went wrong: {error.message}</p> <button onClick={reset}>Retry</button> </div> ); } - not-found.tsx – Rendered when
notFound()is thrown or no segment matches.export default function NotFound() { return <h1>404 – Page Not Found</h1>; }
Key Concepts & Formulas
- Route Segment Resolution – URL segments map 1:1 to folder hierarchy.
- Static segment: folder name matches URL part.
- Dynamic segment:
[param]folder (e.g.,[id]). - Catch‑all:
[...slug]matches one or more segments. - Optional catch‑all:
[[...slug]]matches zero or more segments.
- Server Component Default – All components inside
app/are Server Components unless the'use client'directive is present.'use client'enables interactivity (hooks, browser APIs) and sends JavaScript to the browser. - Layout Composition – Layouts nest from outer to inner:
Root layout → Route Group layout → Segment layout → Page. Each layout wraps itschildrenprop, preserving state across navigations.
6.2 Advanced Routing Patterns
6.2.1 Route Groups
Parentheses folders (group) organize routes without affecting the URL. This enables different layouts for separate sections while keeping a clean path.
- Example:
app/(auth)/login/page.tsxandapp/(auth)/register/page.tsxproduce URLs/loginand/registerbut shareapp/(auth)/layout.tsx. - Use case: Marketing site (
/(marketing)) vs. dashboard (/(dashboard)) with distinct headers/footers.
6.2.2 Parallel Routes
Parallel routes allow rendering multiple independent pages inside the same layout using the @folder convention. Each parallel slot receives a prop in the layout component.
- File structure:
app/ @analytics/ page.tsx @team/ page.tsx layout.tsx - Layout receives props
{ children, analytics, team }:export default function Layout({ children, analytics, team }) { return ( <> <main>{children}</main> <aside> <section>{analytics}</section> <section>{team}</section> </aside> </> ); } - Example: A dashboard showing main content (
children) alongside an analytics panel and a team chat, each navigable independently.
6.2.3 Intercepting Routes
Intercepting routes let you override a route segment while preserving the parent layout, using the (..) convention. This is common for modal‑style navigation.
- Convention:
(.)– same level(..)– parent level(..)(..)– grandparent level(...)– root level
- Example: Photo gallery with modal.
app/ gallery/ page.tsx // /gallery layout.tsx // gallery layout (..)/photo/ [id]/ page.tsx // intercepts /gallery/photo/123 - Clicking a photo in
/gallerynavigates to/gallery/photo/123but the URL shown is/photo/123. Thegallerylayout remains, and the modal (photo/[id]/page.tsx) renders inside it. Refreshing/photo/123falls back to a full‑page version if a corresponding route exists at the root.
Concrete Examples
1. Route Group Layout
app/(dashboard)/layout.tsx provides a sidebar/navigation for all dashboard routes without adding /dashboard to the URL.
// app/(dashboard)/layout.tsx
export default function DashboardLayout({ children }) {
return (
<div className="dashboard">
<nav>Sidebar</nav>
<main>{children}</main>
</div>
);
}
2. Parallel Routes
Rendering a modal alongside main content.
// app/@modal/(.)photo/[id]/page.tsx
export default function PhotoModal({ params }) {
return <div>Photo {params.id}</div>;
}
// app/layout.tsx
export default function Layout({ children, modal }) {
return (
<>
<main>{children}</main>
{modal && <div className="modal-backdrop">{modal}</div>}
</>
);
}
3. Intercepting Route
Showing a photo modal over the gallery while keeping the gallery layout.
// app/gallery/layout.tsx
export default function GalleryLayout({ children }) {
return (
<div>
<h1>Gallery</h1>
<nav>Gallery nav</nav>
<section>{children}</section>
</div>
);
}
// app/gallery/(..)/photo/[id]/page.tsx
export default function PhotoPage({ params }) {
return <div>Photo {params.id} (modal)</div>;
}
4. Loading Sequence
When navigating to a route, loading.tsx appears instantly, then the Server Component streams the final UI.
- User clicks link → Next.js shows
loading.tsx(via Suspense) → Server streams HTML forpage.tsx→ Loading UI replaced.
Best Practices
- Use route groups to isolate layouts (marketing, auth, dashboard) without polluting the URL.
- Prefer Server Components for data fetching and static markup; add
'use client'only when you need hooks, state, or browser APIs. - Leverage
loading.tsxfor perceived performance – show a skeleton or spinner while data loads. - Use
not-found.tsx together withnotFound()in data‑fetching functions to gracefully handle missing resources. - Keep layouts small and avoid heavy client‑side logic; let Server Components do the heavy lifting.
- When using parallel routes, ensure each slot’s content is lightweight to avoid unnecessary re‑renders.
- For intercepting routes, remember that a refresh on the intercepted URL will render the intercepted page’s own layout (if defined) or fall back to a root‑level route.
Summary
The App Router modernizes routing in Next.js by treating the app/ directory as a map of URL segments, introducing special files for layout, loading, error, and not‑found states, and enabling powerful patterns like route groups, parallel routes, and intercepting routes. By defaulting to Server Components, it minimizes JavaScript sent to the browser while preserving rich interactivity through selective client boundaries. Mastering these concepts allows developers to build fast, scalable, and maintainable Next.js applications.