Pages Router vs App Router
Answer
Next.js has two routing systems: the original Pages Router (pages directory) and the newer App Router (app directory) introduced in Next.js 13.
Router Comparison
File Structure
# Pages Router
pages/
├── index.js → /
├── about.js → /about
├── blog/
│ ├── index.js → /blog
│ └── [slug].js → /blog/:slug
├── _app.js → Root wrapper
└── _document.js → HTML document
# App Router
app/
├── page.js → /
├── layout.js → Root layout
├── about/
│ └── page.js → /about
├── blog/
│ ├── page.js → /blog
│ └── [slug]/
│ └── page.js → /blog/:slug
└── loading.js → Loading UI
Data Fetching
// PAGES ROUTER - getServerSideProps (SSR)
export async function getServerSideProps(context) {
const data = await fetchData();
return { props: { data } };
}
export default function Page({ data }) {
return <div>{data.title}</div>;
}
// PAGES ROUTER - getStaticProps (SSG)
export async function getStaticProps() {
const data = await fetchData();
return {
props: { data },
revalidate: 60, // ISR
};
}
// APP ROUTER - Direct async in component
async function Page() {
const data = await fetchData(); // Server Component
return <div>{data.title}</div>;
}
// With caching
async function Page() {
const data = await fetch("/api/data", {
cache: "force-cache", // SSG
// or: next: { revalidate: 60 } // ISR
// or: cache: 'no-store' // SSR
});
}
Layout System
// PAGES ROUTER - _app.js
export default function App({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
// Per-page layouts with getLayout
Page.getLayout = function (page) {
return <DashboardLayout>{page}</DashboardLayout>;
};
// APP ROUTER - layout.js (nested layouts)
// app/layout.js - Root layout
export default function RootLayout({ children }) {
return (
<html>
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
// app/dashboard/layout.js - Dashboard layout
export default function DashboardLayout({ children }) {
return (
<div className="dashboard">
<Sidebar />
<main>{children}</main>
</div>
);
}
Special Files (App Router)
| File | Purpose |
|---|---|
page.js | Route UI |
layout.js | Shared wrapper |
loading.js | Loading UI (Suspense) |
error.js | Error UI |
not-found.js | 404 UI |
route.js | API endpoint |
Key Differences
| Feature | Pages Router | App Router |
|---|---|---|
| Default components | Client | Server |
| Data fetching | getServerSideProps, getStaticProps | async components, fetch |
| Layouts | Manual with getLayout | Native with layout.js |
| Loading states | Manual | loading.js (Suspense) |
| Error handling | _error.js | error.js (per route) |
| Streaming | No | Yes |
| React 18 features | Limited | Full support |
Migration Path
// Start with App Router for new projects
// Migrate gradually - both can coexist
// pages/old-page.js - Still works
// app/new-page/page.js - New routes
// Shared components work in both
// app/components/Button.js
Key Points
- App Router is the recommended default for new projects
- Server Components are default in App Router
- App Router uses folders for routes, Pages uses files
- Layouts in App Router are truly nested and persistent
- Both routers can coexist during migration
- App Router enables streaming and React 18 features