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
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>
}
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>
}
3. API Routes
// pages/api/users.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({ users: ['John', 'Jane'] })
}
}
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
}
}
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
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>
}
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>
)
}
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
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>
)
}
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>
)
}
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)