Mastering Next.js App Router: From Pages to Layouts

Introduction

The Next.js App Router in Next.js 15 introduces a completely new way to manage routing, layouts, and data fetching. Instead of the older Pages Router, the App Router uses a simpler and more scalable structure. It makes development faster, helps organize components cleanly, and improves performance for large web apps.

If you’re building a React-based project or migrating an older Next.js app, understanding the Next.js App Router is essential. This guide will walk you through its setup, structure, and migration process step by step.


Prerequisites

Before using the Next.js App Router, make sure you have:

  • Node.js 18.17+
  • npm or yarn
  • Basic understanding of React and Next.js
  • (Optional) An existing Next.js 14 project to migrate

To verify Node.js:

node -v

If it’s below 18.17, download the latest version from nodejs.org.


Step 1 – Create a New Next.js 15 Project

Let’s start by creating a new project that uses the Next.js App Router by default:

npx create-next-app@latest my-app
cd my-app
npm run dev

You’ll notice a new directory called app/. This folder is the foundation of the new routing system.

Open your browser at http://localhost:3000 to confirm the project runs successfully.


Step 2 – Explore the App Directory Structure

The Next.js App Router uses folders as routes:

app/
 ├─ layout.tsx
 ├─ page.tsx
 ├─ globals.css
 └─ favicon.ico

Each folder is a route segment, and every route can have its own layout, loading, and error files. This modular system improves code reusability and consistency across pages.

For example, the layout.tsx file defines shared headers, navigation bars, and footers.

Key differences from the Page Router:

  • Every folder inside app/ is a route segment.
  • Each segment can have its own layout.tsx, page.tsx, loading.tsx, or error.tsx.
  • Components in the App Router are React Server Components by default, which improves performance by running code on the server.

Step 3 – Migrate from the Pages Router

In older projects, your routes might look like this:

pages/about.js

Using the Next.js App Router, you’ll move it to:

app/about/page.tsx

Example conversion:

Old (Page Router):

export default function About() {
  return <h1>About Us</h1>;
}

New (App Router):

export default function AboutPage() {
  return <h1>About Us</h1>;
}

The folder name (about) defines the route automatically — no need to configure a separate router.


Step 4 – Add Layouts and Nested Routes

Layouts in the Next.js App Router make it easy to share UI between routes.

Example directory:

app/
 ├─ layout.tsx
 ├─ dashboard/
    ├─ layout.tsx
    └─ page.tsx
 └─ settings/
     └─ page.tsx

Root layout.tsx

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <header>My App</header>
        {children}
        <footer>© 2025 My App</footer>
      </body>
    </html>
  );
}

Dashboard layout.tsx

export default function DashboardLayout({ children }) {
  return (
    <section>
      <aside>Sidebar</aside>
      <main>{children}</main>
    </section>
  );
}

Each layout wraps only its direct descendants, allowing deeply nested and modular page hierarchies.


Step 5 – Metadata and SEO Handling

The Next.js App Router replaces <Head> with a cleaner metadata system.

Example:

export const metadata = {
  title: 'About Us – My App',
  description: 'Learn more about our team and mission.',
};

export default function AboutPage() {
  return <h1>About Us</h1>;
}

You can also generate metadata dynamically:

export async function generateMetadata({ params }) {
  const data = await getData(params.id);
  return { title: `${data.name} – Profile` };
}

Step 6 – Server Components and Data Fetching

One of the best parts of the Next.js App Router is how it integrates React Server Components for efficient data fetching:

Example:

export default async function UsersPage() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await res.json();

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

This runs entirely on the server and streams HTML to the client — improving performance and reducing bundle size.

If you need client-side interactivity (e.g., state or effects), add "use client" at the top of your file:

"use client";
import { useState } from "react";

Step 7 – Common Migration Issues and Fixes

IssueCauseSolution
Styles not loadingMissing global CSS importImport globals.css in layout.tsx.
useRouter errorsOld next/router importUse the new next/navigation hooks.
Dynamic routes not workingOld [param].js syntax mismatchKeep same naming ([id]/page.tsx) but move inside app/.
Head component missingnext/head deprecatedUse the new metadata export.

For detailed migration notes, refer to Next.js 15 docs.


Conclusion

Migrating to the App Router unlocks powerful new capabilities in Next.js 15 — including server components, nested layouts, and more predictable SEO configuration.
By understanding its directory structure and adopting the new data-fetching model, you can build cleaner, faster, and more scalable applications.

If you’re starting a new project today, the App Router is the default and recommended choice.

Posted in TutorialsTags:
Write a comment