Next.js App Router vs Pages Router
Next.js ships with two routing systems. The Pages Router arrived with the earliest versions of the framework. The App Router joined later and now serves as the recommended choice for new projects. Knowing both systems helps you read older tutorials, maintain existing applications, and pick the right foundation for a fresh build.
The Everyday View
Picture two floor plans for the same house. The first plan gives every room a single door and one fixed layout. The second plan lets rooms share hallways, lights, and walls with their neighbors. Both plans produce a livable house. The second plan gives you more control over the parts that rooms have in common.
The Pages Router follows the first plan. The App Router follows the second.
Folder Layout Side by Side
Each router reads a different top-level folder. The diagram below shows the same website built with both systems.
Pages Router App Router
----------------------------- -----------------------------
pages/ app/
_app.js (wrapper) layout.js (wrapper)
index.js -> / page.js -> /
about.js -> /about about/
blog/ page.js -> /about
[slug].js -> /blog/hello blog/
api/ [slug]/
hello.js -> /api/hello page.js -> /blog/hello
api/
hello/
route.js -> /api/hello
The Pages Router turns every file into a route. The App Router turns every folder into a route and looks for special files inside it, such as page.js, layout.js, and route.js.
Key Differences
| Feature | Pages Router | App Router |
|---|---|---|
| Route source | Files in pages/ | Folders in app/ |
| Default component type | Client-hydrated React components | Server Components |
| Shared layout | _app.js and _document.js | Nested layout.js files |
| Data loading | getStaticProps, getServerSideProps | Async components with fetch |
| API endpoints | pages/api | Route Handlers in route.js |
| Loading and error screens | Manual code | loading.js and error.js |
| Streaming | Limited | Built in |
Data Fetching Compared
Data loading shows the biggest day-to-day difference. The Pages Router asks you to export a special function. The App Router lets the component fetch its own data.
Pages Router Version
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return { props: { posts } };
}
export default function Blog({ posts }) {
return (
<ul>
{posts.map((p) => <li key={p.id}>{p.title}</li>)}
</ul>
);
}App Router Version
export default async function Blog() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return (
<ul>
{posts.map((p) => <li key={p.id}>{p.title}</li>)}
</ul>
);
}The second version has fewer moving parts. The data call sits right beside the markup that uses it, so a reader follows one path from top to bottom.
Navigation Hooks Differ
Both routers offer a useRouter hook, but the import paths differ. The Pages Router imports from next/router. The App Router imports from next/navigation. Mixing the two paths causes confusing errors, so check the import line whenever a tutorial code sample fails.
Using Both Routers Together
A single project can hold both folders. This setup supports gradual migration. You move one page at a time from pages/ into app/ while the rest of the site keeps working.
One rule applies: two files cannot claim the same URL. If pages/about.js and app/about/page.js both exist, the build stops with an error. Delete the old file once the new version is ready.
Which One to Choose
Choose the App Router When
- You start a new project.
- You want nested layouts that persist between page changes.
- You want Server Components to reduce the JavaScript sent to the browser.
- You plan to use Server Actions, streaming, or Suspense.
Stay With the Pages Router When
- An existing production site already runs on it without problems.
- Your team depends on libraries that do not yet support Server Components.
- The cost of migration outweighs the benefit for your project.
Migration Tips
Move Small Pages First
Start with simple pages such as an About page or a Contact page. These pages carry little data logic and teach you the new folder rules with low risk.
Replace Data Functions Gradually
Convert getStaticProps into an async component with a cached fetch. Convert getServerSideProps into an async component with an uncached fetch. Test each page in the browser before you remove the old file.
Move Shared Layout Last
Rebuild the content of _app.js and _document.js inside app/layout.js after most pages have moved. The root layout must include the html and body tags.
Quick Summary
The Pages Router offers a simple, file-based model with years of community examples. The App Router offers a folder-based model with layouts, Server Components, and streaming. New projects benefit most from the App Router, and existing projects can adopt it one page at a time.
