Next.js App Router vs Pages Router: When to Migrate and When to Stay Put
Next.js Pages Router served us well for years. It is stable, well-documented, and battle-tested. The App Router (introduced in Next.js 13, stable in 14+) offers new capabilities: React Server Components, nested layouts, streaming, and parallel routes. But it is also different enough that migration is not trivial.
Should you migrate? The answer depends on your specific situation. Here is the honest assessment from someone who migrated one production application and chose NOT to migrate another.
What App Router Actually Gives You
Server Components by default: components render on the server with zero client JavaScript. For content-heavy sites (blogs, documentation, directories), this dramatically reduces bundle size. Our tool listing pages send zero JavaScript for the main content — only interactive widgets ship JavaScript.
Nested layouts: define a layout once and it persists across sub-routes without re-rendering. In Pages Router, navigating between /tools and /tools/prisma re-renders the entire page including the shared header and sidebar. In App Router, only the content area re-renders.
Streaming and Suspense: page content loads progressively. The shell renders immediately, then individual sections fill in as their data loads. Users see content faster even when some data sources are slow.
Parallel and intercepting routes: display multiple pages simultaneously (e.g., a modal that shows a tool detail while the listing remains visible underneath). This enables patterns that were awkward or impossible in Pages Router.
Improved data fetching: fetch directly in server components without getServerSideProps/getStaticProps. No page-level waterfall — components fetch their own data in parallel.
What You Lose in Migration
Simplicity: Pages Router is simpler. One file = one page. getServerSideProps for server data. _app.tsx for global layout. The mental model is straightforward.
App Router's mental model is more complex: server vs client boundaries, caching layers, revalidation strategies, and the interaction between server components and client components. The learning curve is real.
Ecosystem compatibility: some libraries designed for Pages Router need adaptation for App Router. Libraries that use client-side hooks (useRouter from next/router) need updates. Some third-party components that rely on client context do not work seamlessly in server components.
Established patterns: your team knows Pages Router deeply. They can debug issues quickly, follow established patterns, and onboard new developers easily. Migration resets this institutional knowledge.
When to Migrate
Migrate if: your application is content-heavy and bundle size is a real performance problem, you need nested layouts (complex dashboard or multi-section applications), you are starting significant new feature work (build new features in App Router, migrate old pages incrementally), or your team has time to invest in learning the new model.
Migrate if the benefits (smaller bundles, better performance, new capabilities) clearly exceed the costs (learning curve, migration effort, temporary instability).
When NOT to Migrate
Stay on Pages Router if: your application works well and performance is acceptable, you are in maintenance mode (not adding significant new features), your team is small and cannot absorb the learning curve while shipping features, the application is heavily interactive (mostly client-side state, few server-rendered pages — the Server Component advantage is minimal), or you rely on libraries that do not support App Router yet.
The honest truth: Pages Router is not deprecated and will be supported long-term. There is no urgency to migrate. A well-built Pages Router application is not inferior — it just uses a different model.
The Incremental Migration Strategy
If you decide to migrate: do it incrementally. App Router and Pages Router coexist in the same Next.js application. You can migrate one route at a time.
Strategy: move new features to App Router (app/ directory), keep existing features in Pages Router (pages/ directory), migrate existing pages one at a time when you touch them for other reasons, and accept that both routers coexist for months.
Start with: your layout (move the shared layout to app/layout.tsx — this gives you nested layouts immediately for app/ routes while pages/ routes still use _app.tsx).
Then migrate: simple content pages first (blog posts, documentation, about pages — these benefit most from Server Components). Dashboard and interactive pages last (they need more "use client" components and benefit least from RSC).
The Decision I Made
For AIDToolStack: we started with App Router because we began building during the App Router era. The benefits are clear for our use case — a content-heavy tool directory with many static pages that benefit from zero-JS server rendering.
For a client project (an internal dashboard with heavy interactive state): we stayed on Pages Router. The dashboard is almost entirely interactive widgets, charts, and forms. Server Components provide minimal benefit because everything is a Client Component anyway. The migration cost was not justified by the minimal performance improvement.
Migration Challenges We Encountered
Data fetching patterns: getServerSideProps returns all page data at once. Server Components fetch data individually per component. This is better (no waterfall) but requires restructuring how you think about data loading.
Client-side navigation state: Pages Router's useRouter provides query params, pathname, and push/replace. App Router splits this: usePathname(), useSearchParams(), and useRouter() (with a different API). Every page using useRouter needs updating.
Authentication patterns: middleware-based auth works the same. But if you passed user data through getServerSideProps, you need a new pattern for Server Components (reading cookies directly, or using a server-side auth helper).
Third-party libraries: some analytics, error tracking, and state management libraries need different initialization for App Router. Sentry, for example, has separate configuration files for client and server in App Router.
Performance Comparison
Our real-world measurements (AIDToolStack, content-heavy site):
Pages Router (before migration would have been): estimated 200KB initial JavaScript bundle, 1.5s Time to Interactive.
App Router (actual): 140KB initial JavaScript bundle (30% reduction due to Server Components), 0.9s Time to Interactive, streaming enables perceived instant navigation.
For a dashboard application (hypothetical migration): the difference would be minimal — 5-10% bundle reduction at best, because most components are interactive and remain Client Components regardless.
The Honest Summary
App Router is the future of Next.js and provides real benefits for content-heavy applications. But it is not a mandatory upgrade. Pages Router is stable, performant, and well-understood.
Migrate when the benefits clearly outweigh the costs for YOUR specific application. Do not migrate because App Router is new or because conference talks say you should. Migrate because your application's characteristics (content-heavy, layout-heavy, performance-sensitive) align with App Router's strengths.
Written by AIDToolStack Editorial
The AIDToolStack editorial team reviews developer tools, AI products, and MCP servers with hands-on testing and honest assessments. We use the tools we review every day in production.
References & Further Reading
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.