DEV Community

Cover image for App Router vs Pages Router in Next.js — a deep, practical guide
Shyamalendu Nayak
Shyamalendu Nayak

Posted on

App Router vs Pages Router in Next.js — a deep, practical guide

Next.js has revolutionized React development with its powerful routing systems. Over the years, it has evolved from the traditional Pages Router to the modern App Router, each offering unique advantages for different use cases. In this comprehensive guide, we'll explore both routing systems, their differences, and help you decide which one to use for your next project.

The Page Router (Legacy)

The Page Router was Next.js's original routing system, introduced from the beginning and still widely used today. It's stable, well-documented, and has been battle-tested in countless production applications.

How Page Router Works
In the Page Router system, every file in the pages directory automatically becomes a route:

pages/
├── index.js          // Route: /
├── about.js          // Route: /about
├── blog/
│   ├── index.js      // Route: /blog
│   └── [slug].js     // Route: /blog/[slug]
└── api/
    └── users.js      // API Route: /api/users
Enter fullscreen mode Exit fullscreen mode

Key Features of Page Router

1. File-Based Routing

// pages/index.js
export default function Home() {
  return <h1>Welcome to the homepage!</h1>
}

// pages/about.js
export default function About() {
  return <h1>About Us</h1>
}
Enter fullscreen mode Exit fullscreen mode

2. Dynamic Routes

// pages/blog/[slug].js
import { useRouter } from 'next/router'

export default function BlogPost() {
  const router = useRouter()
  const { slug } = router.query

  return <h1>Blog Post: {slug}</h1>
}
Enter fullscreen mode Exit fullscreen mode

3. API Routes

// pages/api/users.js
export default function handler(req, res) {
  if (req.method === 'GET') {
    res.status(200).json({ users: ['John', 'Jane'] })
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Data Fetching Methods

// Static Generation
export async function getStaticProps() {
  const data = await fetchData()
  return { props: { data } }
}

// Server-Side Rendering
export async function getServerSideProps() {
  const data = await fetchData()
  return { props: { data } }
}

// Static Generation with Dynamic Routes
export async function getStaticPaths() {
  return {
    paths: [{ params: { slug: 'post-1' } }],
    fallback: false
  }
}
Enter fullscreen mode Exit fullscreen mode

Advantages of Page Router

  • Mature and Stable: Years of development and community usage
  • Simple Mental Model: Easy to understand file-to-route mapping
  • Extensive Documentation: Comprehensive guides and tutorials available
  • Wide Community Support: Large ecosystem of tutorials and solutions
  • Proven in Production: Used by countless large-scale applications

The App Router (Modern)

Introduced in Next.js 13, the App Router represents a significant evolution in Next.js routing. Built on React Server Components, it offers more flexibility, better performance, and modern React patterns.

How App Router Works
The App Router uses an app directory where folders define routes, and special files define UI components:

app/
├── layout.js         // Root layout
├── page.js          // Route: /
├── about/
│   └── page.js      // Route: /about
├── blog/
│   ├── layout.js    // Blog layout
│   ├── page.js      // Route: /blog
│   └── [slug]/
│       └── page.js  // Route: /blog/[slug]
└── api/
    └── users/
        └── route.js // API Route: /api/users
Enter fullscreen mode Exit fullscreen mode

Key Features of App Router

1. Server Components by Default

// app/page.js (Server Component)
async function getData() {
  const res = await fetch('https://api.example.com/data')
  return res.json()
}

export default async function HomePage() {
  const data = await getData()

  return <h1>Welcome! {data.message}</h1>
}
Enter fullscreen mode Exit fullscreen mode

2. Layouts and Templates

// app/layout.js (Root Layout)
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <nav>Navigation</nav>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  )
}

// app/blog/layout.js (Nested Layout)
export default function BlogLayout({ children }) {
  return (
    <div className="blog-layout">
      <aside>Blog Sidebar</aside>
      <main>{children}</main>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

3. Route Groups and Organization

app/
├── (marketing)/
│   ├── about/
│   │   └── page.js    // Route: /about
│   └── contact/
│       └── page.js    // Route: /contact
└── (shop)/
    ├── products/
    │   └── page.js      // Route: /products
    └── cart/
        └── page.js      // Route: /cart
Enter fullscreen mode Exit fullscreen mode

4. Loading and Error UI

// app/blog/loading.js
export default function Loading() {
  return <div>Loading blog posts...</div>
}

// app/blog/error.js
'use client'
export default function Error({ error, reset }) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={() => reset()}>Try again</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

5. Streaming and Suspense

// app/dashboard/page.js
import { Suspense } from 'react'

async function UserData() {
  const data = await fetchUserData() // This can stream
  return <div>{data.name}</div>
}

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<div>Loading user...</div>}>
        <UserData />
      </Suspense>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Advantages of App Router

  • React Server Components: Better performance and smaller bundle sizes
  • Streaming: Progressive page rendering for better user experience
  • Flexible Layouts: Nested layouts that persist across route changes
  • Built-in Loading States: Automatic loading and error boundaries
  • Better SEO: Improved server-side rendering capabilities
  • Modern React Patterns: Leverages the latest React features

Key Differences Comparison

Features Page Router App Router
Directory pages/ app
Routing Method File-based Folder based
Components Client by default Server by default
Date fetching getServerSideProps, getStaticProps fetch() in components
Layouts _app,_document.js layout.js
Loading state Custom implementation Built-in loading.js
Bundle Size Larger client bundles Smaller client bundles

Conclusion
Both Page Router and App Router have their place in the Next.js ecosystem. The Page Router remains a solid choice for many applications, offering stability and simplicity. The App Router, while more complex, provides powerful features that can significantly improve performance and developer experience.

Feel free to checkout my GitHub

Top comments (0)