The App Router: A Modern Way to Build React Applications
Leeting Yan
The App Router is one of the most important ideas in modern Next.js.
It replaces the old Pages Router with a more flexible, more intuitive, and more scalable way to structure your application.
Instead of thinking in terms of “pages with lifecycle functions,” you design your app in terms of composable layouts, nested routes, and Server/Client Components working together.
In this chapter, we’ll explore what the App Router is, why it matters, and how to use it without confusion or unnecessary complexity.
Why the App Router exists
React has been evolving toward:
- Server-first rendering
- Composable UI instead of global frameworks
- Streaming and parallel fetching
- Better code splitting and performance guarantees
The Pages Router (the old pages/ directory) wasn’t designed for these ideas.
It relied heavily on client-side React concepts and had limited support for nested layouts, streaming, or server-only components.
The App Router solves these problems by introducing:
- File-based routing with nested layouts
- Server Components by default
- Shared UI that persists between navigations
- Streaming rendering for faster page loads
- Clear separation between server logic and client logic
Think of it as a more “React-native” approach to both UX and code structure.
The mental model: your folder structure is your app
With the App Router, the structure of your app/ directory becomes the shape of your actual application.
For example:
app/
layout.tsx
page.tsx
dashboard/
layout.tsx
page.tsx
settings/
page.tsx
This creates routes:
/→app/page.tsx/dashboard→app/dashboard/page.tsx/dashboard/settings→app/dashboard/settings/page.tsx
And each folder can optionally define:
page.tsx→ the route’s UIlayout.tsx→ shared UI that wraps childrenloading.tsx→ fallback UI while content loadserror.tsx→ per-route error boundaryroute.ts→ API endpoint for that segmentnot-found.tsx→ 404 for that segment only
Instead of a global routing config, you express intent through folders and files.
This makes large apps much easier to reason about.
Layouts: the core idea that makes everything feel “structured”
In traditional React apps, navigating between pages usually causes the entire app to re-render.
In Next.js App Router, layouts persist across navigations, meaning your UI is more stable and more predictable.
Example:
app/
layout.tsx → global layout
dashboard/
layout.tsx → dashboard-only layout
page.tsx → dashboard main page
When navigating between dashboard pages, the dashboard layout stays mounted, keeping:
- sidebar open,
- navigation stable,
- context preserved.
This is especially helpful for dashboards, SaaS apps, developer tools, and admin panels — the kind of apps where UI consistency matters.
Page components: simple, server-first UI
A simple page looks like this:
export default function Page() {
return <h1>Welcome</h1>;
}
By default, it’s a React Server Component.
This means:
- It runs on the server.
- It can access databases, files, environment variables.
- It loads faster, with smaller bundles.
- It can stream partial UI to the user.
If you need client-side interactivity:
"use client";
export default function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}
The "use client" directive tells Next.js that this component:
- Will run in the browser,
- Can use state, effects, refs,
- Must be bundled and hydrated.
The key pattern:
- Use Server Components for most UI.
- Use Client Components only where interactivity is needed.
This keeps your application fast without losing flexibility.
Nested routing: natural composition
Nested routing is a core superpower of the App Router.
Example:
app/
docs/
layout.tsx
page.tsx
getting-started/
page.tsx
api/
page.tsx
This becomes:
/docs/docs/getting-started/docs/api
Each folder can define its own layout, which wraps all children.
This enables building entire documentation sites or large hierarchies with very little routing code.
Route segments: placeholders in the folder structure
Dynamic routes are expressed through brackets:
app/
blog/
[slug]/
page.tsx
This becomes:
/blog/my-post/blog/nextjs-basics/blog/anything-here
And you can access the segment:
export default function Page({ params }) {
return <h1>{params.slug}</h1>;
}
This keeps routing declarative and simple.
Loading states, errors, and not-found pages (per segment)
One of the elegant benefits of the App Router is local boundaries.
You can define:
loading.tsx→ skeleton while fetchingerror.tsx→ localized error boundarynot-found.tsx→ segment-specific 404
Example:
app/
products/
loading.tsx
error.tsx
[id]/
page.tsx
This makes UX smoother and debugging easier — a small error in one part of the UI won’t blank the whole screen.
How this changes how we build applications
The App Router encourages a shift in mindset:
From:
- “Pages reload and state resets.”
- “Everything is a client component.”
- “Routing is external configuration.”
- “Data fetching is manual and fragile.”
To:
- “Layouts stay stable across navigations.”
- “Most components run on the server.”
- “Routing is your folder structure.”
- “Data loading is colocated with UI segments.”
It’s a more holistic and expressive way to build with React.
Best practices (Birdor-style, practical, calm)
- Keep your folder hierarchy clean and meaningful.
- Use layouts to enforce structure, not glue code.
- Prefer Server Components unless you need interactivity.
- Avoid deep nesting unless your UI truly demands it.
- Keep client logic small and focused.
- Don’t overthink — start simple and let your app grow naturally.
The App Router is powerful, but it rewards clarity over cleverness.
Summary
The App Router brings structure, clarity, and performance to the modern React application.
By aligning routing with folder structure and introducing persistent layouts, it helps you build more scalable and predictable apps.
It’s a new mental model, but once it clicks, building in Next.js feels more natural and less repetitive.
In the next chapter, we’ll explore file-based routing in more depth — including dynamic routes, parallel routes, and effective folder organization for real-world apps.
Your application will feel more stable, more expressive, and easier to evolve — all while staying close to React’s core ideas.