CodeWithSabir
HomeAIDevOpsNext.jsMobile DevelopmentWeb Development
CodeWithSabir
  • Home
  • AI
  • DevOps
  • Next.js
  • Mobile Development
  • Web Development
  • About
  • Contact
CodeWithSabir

In-depth articles, tutorials, and guides on web development, React, Next.js, AI, and modern programming practices.

Topics

  • AI
  • DevOps
  • Next.js
  • Mobile Development
  • Web Development

Company

  • About
  • Contact
  • Privacy Policy
  • Terms

© 2026 CodeWithSabir. All rights reserved.

Built with SabirSoft.com

Home/Next.js/Next.js App Router vs Pages Router: Which Should You Use?
Next.js

Next.js App Router vs Pages Router: Which Should You Use?

A practical comparison of Next.js App Router and Pages Router — when to migrate, what actually changed, and which one makes sense for your project in 2026.

Sabir KhaloufiSabir KhaloufiFebruary 5, 20264 min read

The App Router was introduced in Next.js 13 and became stable in 14. In 2026, it's the default for new projects. But understanding what actually changed — and when the Pages Router is still acceptable — helps you make better architectural decisions.

What Actually Changed

The App Router isn't just a new file structure. It's a fundamentally different rendering model based on React Server Components (RSC). The Pages Router renders everything as client-side React with optional server-side data fetching functions. The App Router treats server rendering as the default and client rendering as opt-in.

Pages RouterApp Router
Default component typeClient ComponentServer Component
Data fetchinggetServerSideProps, getStaticPropsasync component functions
LayoutsManual _app.tsxNested layout.tsx files
Loading statesManualloading.tsx convention
Error handling_error.tsxerror.tsx per segment
StreamingNot supportedBuilt-in with Suspense
API routespages/api/*app/api/*/route.ts

File Structure Comparison

code
# Pages Router
pages/
├── _app.tsx          # Global layout
├── _document.tsx     # HTML document
├── index.tsx         # / route
├── about.tsx         # /about route
├── blog/
│   ├── index.tsx     # /blog route
│   └── [slug].tsx    # /blog/:slug route
└── api/
    └── posts.ts      # /api/posts endpoint

# App Router
app/
├── layout.tsx        # Root layout (replaces _app + _document)
├── page.tsx          # / route
├── about/
│   └── page.tsx      # /about route
├── blog/
│   ├── layout.tsx    # Shared blog layout
│   ├── page.tsx      # /blog route
│   ├── loading.tsx   # Loading UI
│   └── [slug]/
│       └── page.tsx  # /blog/:slug route
└── api/
    └── posts/
        └── route.ts  # /api/posts endpoint

Data Fetching: The Biggest Difference

Pages Router

typescript
// pages/blog/[slug].tsx
export async function getServerSideProps({ params }) {
  const post = await getPostBySlug(params.slug)
  if (!post) return { notFound: true }
  return { props: { post } }
}
 
export default function BlogPost({ post }) {
  // post is always available — no loading state needed
  return <article>{post.title}</article>
}
typescript
// Static generation with Pages Router
export async function getStaticProps({ params }) {
  const post = await getPostBySlug(params.slug)
  return { props: { post }, revalidate: 3600 }
}
 
export async function getStaticPaths() {
  const slugs = await getAllPostSlugs()
  return { paths: slugs.map(slug => ({ params: { slug } })), fallback: 'blocking' }
}

App Router

typescript
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  return getAllPostSlugs().map(slug => ({ slug }))
}
 
// The component IS the data fetching — no separate function needed
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPostBySlug(params.slug)
  if (!post) notFound()
 
  return <article>{post.title}</article>
}

The App Router eliminates the mental overhead of matching data fetching functions to components. The component fetches its own data and renders it — simpler to read and reason about.

Nested Layouts

This is where App Router has a genuine advantage for complex UIs:

typescript
// app/dashboard/layout.tsx — wraps all /dashboard/* routes
export default function DashboardLayout({ children }) {
  return (
    <div className="flex">
      <Sidebar />
      <main className="flex-1">{children}</main>
    </div>
  )
}
 
// app/dashboard/settings/layout.tsx — nested inside dashboard layout
export default function SettingsLayout({ children }) {
  return (
    <div>
      <SettingsTabs />
      {children}
    </div>
  )
}

In Pages Router, you'd implement this manually in each page or through complex _app.tsx logic. With App Router, the file system defines the layout hierarchy.

Route Handlers vs API Routes

typescript
// Pages Router: pages/api/posts.ts
export default function handler(req, res) {
  if (req.method === 'GET') {
    res.json({ posts: [] })
  }
}
 
// App Router: app/api/posts/route.ts
export async function GET(request: Request) {
  return Response.json({ posts: [] })
}
 
export async function POST(request: Request) {
  const body = await request.json()
  // ...
  return Response.json({ success: true }, { status: 201 })
}

Route Handlers use the web standard Request/Response APIs instead of Node.js req/res. They can also run on the Edge Runtime.

When to Use Pages Router

Despite App Router being the default, Pages Router is still appropriate when:

  1. You have a large existing codebase — migrating a 200-page Pages Router app to App Router is a significant project. Run both concurrently instead (pages/ and app/ can coexist).

  2. Your team is new to React Server Components — the RSC model requires a shift in mental model. For teams under tight deadlines, Pages Router might be safer short-term.

  3. Heavy third-party dependencies — some libraries still assume a browser DOM and don't work in Server Components. Context-heavy UI libraries are a common pain point.

When to Use App Router

For any new project started in 2026: use App Router. The reasons:

  • Server Components reduce JavaScript bundle size significantly
  • Built-in streaming prevents slow data from blocking the whole page
  • Nested layouts make complex UI hierarchies maintainable
  • The direction Next.js is investing in — Pages Router gets bug fixes, not new features

Migration Strategy

If you're on Pages Router and want to migrate incrementally:

  1. Keep pages/ intact — both routers can coexist
  2. Move layout logic to app/layout.tsx first
  3. Migrate one route at a time, starting with simple pages
  4. Move API routes to app/api/ as you touch them
  5. Remove pages/ once all routes are migrated
code
# Both directories work simultaneously
pages/
  legacy-page.tsx     # Still works
app/
  new-page/
    page.tsx          # New page works alongside legacy

Common Mistakes When Migrating

1. Adding 'use client' everywhere. This defeats the purpose of App Router. Only use it when you need hooks or browser APIs.

2. Forgetting async on data-fetching components. Server Components can be async — use it.

3. Nesting Client Components around Server Components. Client Components can't import Server Components. Pass them as children instead.

4. Expecting getServerSideProps patterns to work. App Router has no getServerSideProps. Data fetching happens in the component itself.

Key Takeaways

  • App Router is the right choice for all new Next.js projects in 2026
  • The key change is the rendering model: Server Components by default, Client Components opt-in
  • Nested layouts in App Router are cleaner and more maintainable than Pages Router approaches
  • Pages Router and App Router can coexist — migrate incrementally rather than all at once
  • Route Handlers use web standard Request/Response — more portable than Pages Router API routes
#next.js#app router#pages router#react#migration
Share:
Sabir Khaloufi — author photo

Written by

Sabir Khaloufi

Full-stack developer and tech blogger sharing in-depth tutorials on React, Next.js, AI, and modern web development.

On this page

  • What Actually Changed
  • File Structure Comparison
  • Data Fetching: The Biggest Difference
  • Pages Router
  • App Router
  • Nested Layouts
  • Route Handlers vs API Routes
  • When to Use Pages Router
  • When to Use App Router
  • Migration Strategy
  • Common Mistakes When Migrating
  • Key Takeaways

Related Articles

Next.js

How to Build a Fullstack App with Next.js 15: Complete Guide

A hands-on guide to building a fullstack application with Next.js 15 — covering App Router, Server Actions, database integration, authentication, and deployment.

Sabir KhaloufiFebruary 25, 20265 min read
Next.js

Next.js Server Components Explained: What They Are and Why They Matter

A clear explanation of React Server Components in Next.js — what problem they solve, how they differ from Client Components, and the patterns that make them powerful.

Sabir KhaloufiFebruary 20, 20264 min read
Next.js

Authentication in Next.js with NextAuth.js v5: The Complete Setup

Learn how to implement authentication in Next.js using NextAuth.js v5 — covering credentials, OAuth providers, JWT sessions, protected routes, and role-based access control.

Sabir KhaloufiFebruary 15, 20264 min read