The App Router: A Modern Way to Build React Applications

A calm, practical introduction to the Next.js App Router — how it works, why it exists, and how to structure modern React applications with clarity.

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
  • /dashboardapp/dashboard/page.tsx
  • /dashboard/settingsapp/dashboard/settings/page.tsx

And each folder can optionally define:

  • page.tsx → the route’s UI
  • layout.tsx → shared UI that wraps children
  • loading.tsx → fallback UI while content loads
  • error.tsx → per-route error boundary
  • route.ts → API endpoint for that segment
  • not-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 fetching
  • error.tsx → localized error boundary
  • not-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.

Keep Reading

Follow the engineering thread

Get the next practical Birdor note, or browse the archive for related systems, tooling, and architecture work.

Join newsletter Browse articles