ProgrUmar Logo
Module 1: Foundations & Routing

Introduction to the App Router

Duration: 12 mins

Welcome to Next.js

In this lesson we explore the App Router paradigm introduced in Next.js 13 and refined through versions 14 and 15. You'll learn why Vercel made this shift and what it means for your day-to-day development.

Key Concepts

  • The app/ directory vs the old pages/ directory
  • Layouts, Pages, and Templates
  • The role of React Server Components (RSC)

By the end of this lesson you will feel comfortable navigating a fresh Next.js project and understanding where each file type belongs.

Introduction to the App Router

If you've used Next.js before version 13, you built everything inside a pages/ folder. Each file became a route, data fetching happened through getServerSideProps or getStaticProps, and layouts were stitched together manually using _app.tsx. It worked — but it had real limitations as apps grew larger.

In Next.js 13, Vercel introduced a completely new way of building apps: the App Router. It lives inside an app/ folder alongside your existing pages/ folder (if you have one), and it's built on top of the latest React features — most importantly React Server Components. By Next.js 15, the App Router is the default and recommended approach for all new projects.

In this lesson you'll understand why this shift happened, what's actually different under the hood, and how to navigate a freshly created Next.js project with confidence.


1. What Was Wrong With the Pages Router?

The Pages Router was great for its time, but a few pain points kept coming up:

  • Layouts were awkward. If you wanted a sidebar to persist across a set of pages, you had to wrap every page manually or do gymnastics inside _app.tsx. Nested layouts — like a dashboard layout inside an admin layout — were especially painful.
  • Data fetching was tied to the page. getServerSideProps only ran at the top-level page component. If a deeply nested component needed server data, you had to fetch it at the top and drill it down as props. That made components hard to move around.
  • Every component shipped JavaScript to the browser. Even components that just rendered static HTML — a blog post, a product description, a footer — sent their JS to the client, where it was hydrated. This added weight to every page.

The App Router solves all three of these problems.


2. The App Router's Core Ideas

2a. The app/ Directory

All App Router code lives inside the app/ folder at the root of your project. Here's what a minimal Next.js 15 project looks like:

my-app/
├── app/
│   ├── layout.tsx        ← Root layout (required)
│   ├── page.tsx          ← Homepage "/"
│   ├── about/
│   │   └── page.tsx      ← "/about"
│   └── blog/
│       ├── layout.tsx    ← Layout for all /blog/* pages
│       └── [slug]/
│           └── page.tsx  ← "/blog/my-post-title"
├── public/
├── next.config.ts
└── package.json

The folder structure is your URL structure. Want a route at /about? Create app/about/page.tsx. Want /blog/any-post-title? Create app/blog/[slug]/page.tsx. No extra configuration needed.

2b. Special File Names

Inside any folder in app/, Next.js looks for files with specific names. Each one has a distinct job:

File What it does
page.tsx The UI for this route. Makes the route publicly accessible.
layout.tsx A wrapper that persists across all child routes without re-rendering.
loading.tsx A skeleton/spinner shown instantly while the page loads.
error.tsx A fallback UI shown when an error is thrown inside this route.
not-found.tsx Shown when notFound() is called or the route doesn't exist.
route.ts An API endpoint (like a REST handler). No UI.

You don't need all of these in every folder — only create the ones your route actually needs.

2c. React Server Components — The Big Shift

This is the most important concept in the App Router. In Next.js 15, every component inside app/ is a React Server Component (RSC) by default.

What does that mean in practice?

  • The component runs only on the server. It has access to your database, file system, and environment variables directly.
  • Zero JavaScript is sent to the browser for that component. The server renders it to HTML and ships the HTML. Your bundle stays small.
  • You can use async/await directly inside the component to fetch data — no useEffect, no useState, no loading spinner boilerplate.

Here's a simple Server Component that fetches and displays a list of posts:

// app/blog/page.tsx
// No "use client" at the top = Server Component by default

export default async function BlogPage() {
  // This fetch runs on the server. The browser never sees this code.
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
  const posts = await res.json();

  return (
    <main>
      <h1>Blog</h1>
      <ul>
        {posts.map((post: { id: number; title: string }) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  );
}

That's it. No getServerSideProps. No API route in the middle. The component itself is async, it fetches, and it renders — all on the server.

2d. Client Components

Server Components can't use browser APIs, event handlers (onClick), or React hooks like useState and useEffect. When you need those things, you opt a component into the client by adding "use client" as the very first line of the file:

// components/Counter.tsx
"use client"; // ← This line makes it a Client Component

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Client Components work exactly like React components always have — they just live inside a Next.js App Router project. The key insight is that you now choose which components go to the browser instead of everything going by default.


3. Nested Layouts — One of the Best Features

Remember how layouts were painful in the Pages Router? The App Router makes them trivial. Each folder can have its own layout.tsx that wraps all routes inside that folder. Layouts nest automatically.

Let's say you're building a dashboard. Here's the folder structure:

app/
├── layout.tsx          ← Wraps everything (nav, footer)
├── page.tsx            ← Homepage
└── dashboard/
    ├── layout.tsx      ← Wraps all /dashboard/* routes (sidebar)
    ├── page.tsx        ← /dashboard
    └── settings/
        └── page.tsx    ← /dashboard/settings

And here's what those layout files look like:

// app/layout.tsx — Root layout
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <nav>My App Nav</nav>
        {children}  {/* page content goes here */}
      </body>
    </html>
  );
}
// app/dashboard/layout.tsx — Dashboard layout
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <div style={{ display: "flex" }}>
      <aside>Sidebar</aside>
      <main>{children}</main>
    </div>
  );
}

When a user visits /dashboard/settings, Next.js renders: RootLayout → DashboardLayout → SettingsPage — all nested automatically. The nav and sidebar persist across navigations without re-rendering or flashing.


4. Creating Your First Next.js 15 App

Run this command in your terminal to scaffold a new project:

npx create-next-app@latest my-app

You'll be asked a few questions. Here's what to pick for this course:

✔ Would you like to use TypeScript? → Yes
✔ Would you like to use ESLint? → Yes
✔ Would you like to use Tailwind CSS? → Yes
✔ Would you like your code inside a src/ directory? → No
✔ Would you like to use App Router? → Yes  ← Important!
✔ Would you like to use Turbopack for next dev? → Yes
✔ Would you like to customize the import alias? → No

Once it finishes, open the project:

cd my-app
npm run dev

Visit http://localhost:3000 in your browser. You'll see the default Next.js welcome page. Now open the project in your code editor and look at app/page.tsx — that's the file rendering what you see.

Delete everything inside it and replace it with this to make sure things are working:

// app/page.tsx
export default function HomePage() {
  return (
    <main>
      <h1>Hello, Next.js 15!</h1>
    </main>
  );
}

Save the file. The browser updates instantly thanks to Turbopack's fast hot reload.


5. Understanding the Generated File Structure

After scaffolding, your project looks like this. Let's go through each important part:

my-app/
├── app/
│   ├── favicon.ico       ← Browser tab icon
│   ├── globals.css       ← Global CSS (Tailwind imports live here)
│   ├── layout.tsx        ← Root layout — wraps every page
│   └── page.tsx          ← The homepage "/"
├── public/               ← Static files (images, fonts, etc.)
├── next.config.ts        ← Next.js configuration
├── tailwind.config.ts    ← Tailwind configuration
├── tsconfig.json         ← TypeScript configuration
└── package.json

Notice there's no pages/ folder. This is a pure App Router project. The public/ folder is served at the root — so public/logo.png is accessible at http://localhost:3000/logo.png.


6. Common Gotchas for Beginners

  • Don't confuse page.tsx with layout.tsx. A page.tsx is the actual content of the route. A layout.tsx is the wrapper around it. A folder without a page.tsx is not a public route.
  • "use client" is contagious downward, not upward. If a parent component is a Server Component, its children can be either. But if a component is a Client Component, all components it imports that aren't separately marked will also be treated as Client Components.
  • The root layout.tsx must have <html> and <body> tags. Next.js will throw an error if these are missing from app/layout.tsx.
  • You can still use pages/ alongside app/. They coexist during migration. Just don't put the same route in both — it causes conflicts.

Key Takeaways

  • The App Router replaces the Pages Router and lives in the app/ directory.
  • Every component is a Server Component by default — add "use client" only when you need interactivity.
  • Special files like page.tsx, layout.tsx, loading.tsx, and error.tsx each have a specific role.
  • Nested layouts are automatic — folder structure defines nesting.
  • Data fetching happens directly inside async Server Components — no more getServerSideProps.

In the next lesson, we'll go deep on file-based routing — dynamic segments, catch-all routes, route groups, and more.

Chat with us