App Router vs Pages Router: a practical comparison
Use the App Router for new Next.js projects. Keep a working Pages Router site unless a feature you need is App Router only. The differences, side by side.
By Sohail Hussain5 November 20266 min read
For a new Next.js project, use the App Router. It is where every new feature lands, it ships less JavaScript by default, and the Next.js documentation treats it as the main router. For an existing Pages Router site that works, there is usually no need to migrate now. Move when you need something only the App Router has, or when you are rebuilding a large part of the site anyway.
The rest of this post compares the two on the points that matter in a real project, and sets out when a migration earns its cost.
What is the difference, in one paragraph?
Both routers turn files into URLs. The Pages Router (the pages/ directory) has been in Next.js since the start: each file is a page, data comes from getStaticProps or getServerSideProps, and every page is a React component that runs in the browser after the first render. The App Router (the app/ directory) arrived in Next.js 13 and is built on React Server Components: folders define routes, page.tsx and layout.tsx files define the UI, components run on the server by default, and only components marked "use client" ship JavaScript. As of October 2026 the current major version is Next.js 16, and both routers are supported in it.
If you are not a developer, the choice is one your developer should make, but it affects cost and speed. We build new sites on the App Router as part of our website design and development work, and why we build company websites with Next.js explains the wider reasoning.
How do the two routers compare feature by feature?
App Router (app/) | Pages Router (pages/) | |
|---|---|---|
| Route files | Folders with page.tsx, shared layout.tsx | One file per page, _app and _document |
| Nested layouts | Built in; layouts persist across navigation | Manual, with a getLayout pattern |
| Default component type | Server Component, no client JavaScript | Client component, hydrated in the browser |
| Data fetching | async components, fetch, use cache | getStaticProps, getServerSideProps, getStaticPaths |
| Updating static pages | cacheTag, revalidateTag, revalidatePath | revalidate and on-demand revalidation in API routes |
| SEO tags | Metadata API, plus sitemap.ts, robots.ts, opengraph-image.tsx | next/head in each page |
| Loading and error states | loading.tsx, error.tsx, not-found.tsx per segment | _error, 404, manual loading UI |
| Server endpoints | Route Handlers (route.ts) | API Routes (pages/api) |
| Forms and mutations | Server Actions | API Routes plus client fetch |
| Internationalised routing | DIY with a [lang] segment and proxy.ts | Built-in i18n config |
| React version | React canary releases bundled with Next.js | The React version in your package.json |
Two rows deserve a comment.
Default component type is the one that changes page weight. A Pages Router page sends the JavaScript for every component on it, even a static footer. An App Router page sends JavaScript only for the interactive parts. On a content-heavy marketing site that difference is often large, and it shows up in Interaction to Next Paint and in load time on mid-range phones.
Internationalised routing is the one place the older router is simpler. The Pages Router has an i18n block in next.config that handles locale prefixes and detection. The App Router removed it; you build the same thing with a [lang] route segment and a small proxy file. Internationalised routing in Next.js shows the setup.
Which router is faster?
On the same content, an App Router site is usually lighter, because Server Components send HTML and no component code. Pages are prerendered at build time in both routers when the data allows it, so time to first byte is similar.
The App Router also streams. A page can send its header and static content immediately and fill in a slow section when its data arrives, using <Suspense>. The Pages Router waits for getServerSideProps to finish before sending anything.
The App Router is not automatically faster if it is used badly. Marking the root layout "use client" turns the whole tree back into client components and loses the benefit. So does wrapping every page in a client-side provider that holds all the data. When we review App Router sites that score poorly, this is the usual cause.
What is harder in the App Router?
Being honest about the costs matters more than the feature list.
The mental model is new. Developers have to know which code runs on the server, which runs in the browser, and what can cross between them. Props passed to a Client Component must be serialisable, so you cannot pass an ordinary function or a class instance from a Server Component. This trips up teams in their first month.
Some libraries assume the browser. Anything that uses React context, useState or window has to sit inside a "use client" boundary. Most popular libraries now handle this, but older UI kits and analytics wrappers sometimes need a small wrapper file.
Caching has changed more than once. Next.js 15 reversed the fetch caching defaults of versions 13 and 14, and Next.js 16 added Cache Components, where you mark cached work with use cache and give it a lifetime with cacheLife. Tutorials from 2023 and 2024 are often out of date. Check the current Next.js caching docs before copying a pattern.
Built-in i18n is gone, as covered above.
None of these is a reason to start a new project on the Pages Router. They are reasons to budget a little learning time if your team has not used the App Router before.
Should you migrate an existing Pages Router site?
Usually not as a project of its own. A working Pages Router site gets security fixes and runs on Next.js 16. Migrating costs developer time and carries the normal risks of any rebuild, including SEO risk if URLs or metadata change by accident.
Migrate when one of these is true:
- You need a feature that only exists in the App Router: nested layouts that keep state across pages, streaming, Server Actions, the Metadata API's file-based sitemaps and share images, or Cache Components.
- Client JavaScript is hurting Core Web Vitals and most of it is for content that does not need to be interactive.
- You are redesigning or rebuilding a large section anyway, so the extra cost of moving it is small.
The two routers can run in one project, so migration can go section by section. The App Router migration guide maps each Pages Router pattern to its replacement: getStaticPaths becomes generateStaticParams, _app and _document become the root layout.tsx, next/head becomes metadata, and API routes become Route Handlers. One catch: links between a page in pages/ and a page in app/ cause a full page load, so move closely linked sections together.
If a migration is on the table, write the redirect and metadata checklist before writing any code. Our website redesign and migration work starts there, and Next.js for SEO lists what to verify after the switch. When you rebuild the head tags, the meta tags generator gives you a complete set to compare against what the old next/head blocks produced.
Was this guide useful?