WritingNext.js App Router vs Pages Router: When to Migrate and What Changes — Clixo
6 min readnext.js, app-router, pages-router, migration, comparison

Next.js App Router vs Pages Router: When to Migrate and What Changes

A clear comparison of the Next.js App Router vs Pages Router — the architectural differences, migration considerations, and when it actually makes sense to switch.

Teams building on Next.js today face a concrete decision: stay on the Pages Router, migrate to the App Router, or run both in the same codebase while migrating incrementally. The Vercel team has been clear that the App Router is the future of Next.js, but that does not mean migrating immediately is always the right call. The two routers are different enough that a migration is not a refactor — it is closer to rewriting the application's architecture.

This post lays out the differences plainly, covers what a migration actually involves, and helps you decide whether to move now or wait.

The Fundamental Architectural Difference

The Pages Router is a file-based routing system built on top of traditional React. Every file in pages/ exports a React component that Next.js renders. Data fetching happens through special exported functions: getServerSideProps for server-side data, getStaticProps for static data, and getStaticPaths for dynamic static routes.

The App Router is a different runtime. It is built on React Server Components, which means components run on the server by default and never ship JavaScript to the client unless they explicitly need to. The file conventions are different, the data fetching model is different, and the caching behavior is different.

These are not shallow differences that a script can translate. They reflect genuinely different approaches to how a Next.js application renders.

What Changed Between the Two

Data Fetching

Pages Router: Data fetching happens through exported functions at the page level. getServerSideProps runs on every request. getStaticProps runs at build time. Components themselves cannot fetch data — they receive it as props.

App Router: Any server component can await data directly in its body. There are no special exported functions. Data fetching is distributed across the component tree rather than centralized at the page level. This removes prop-drilling but requires a new mental model.

Caching

Pages Router: Caching is configured at the page level through getStaticProps and ISR via the revalidate export. No component-level caching.

App Router: Caching is granular. Individual fetch calls, database queries wrapped in unstable_cache, and entire components can be cached with separate revalidation windows. The system is more powerful but also more complex to reason about.

Layouts

Pages Router: Layouts are implemented by wrapping components in _app.tsx or by composing layout components manually in each page. Nested layouts require manual composition.

App Router: layout.tsx files are a first-class primitive. Layouts nest automatically based on the folder hierarchy. Each layout segment stays mounted during navigation within its scope — the sidebar does not remount when navigating between dashboard pages.

API Routes

Pages Router: API routes live in pages/api/. They export handler functions and work like Express handlers.

App Router: API routes are Route Handlers (route.ts files) that export named HTTP method functions (GET, POST, etc.). Server Actions handle most mutation use cases that previously required API routes.

What the Migration Actually Involves

Both routers can coexist in the same Next.js project. You can run the App Router and Pages Router simultaneously during a migration — pages in the pages/ directory continue to work while you move routes to app/ incrementally.

The practical work of migration includes:

Rewriting data fetching. Every page that uses getServerSideProps or getStaticProps needs to be rewritten as an async server component. The logic is often similar, but the structure is entirely different.

Converting layouts. Custom layout wrappers in _app.tsx become layout.tsx files. This often requires splitting shared layout code into the right level of the hierarchy.

Auditing client vs server boundaries. Interactive components need use client. State management, context providers, and anything using hooks needs to be identified and handled explicitly.

Replacing useRouter usage. The useRouter from next/router (Pages Router) is different from the one in next/navigation (App Router). Programmatic navigation code needs updating.

Testing caching behavior. The App Router's caching model is more aggressive than the Pages Router's. Pages that should be dynamic need explicit configuration to avoid serving stale content.

When to Migrate

Migrate now if:

  • You are starting a new project (use the App Router from the beginning)
  • Your project is small enough that a full rewrite is feasible in a sprint or two
  • You want streaming, granular caching, or Server Actions and are willing to invest in the migration

Wait or migrate incrementally if:

  • Your application is large and the Pages Router serves it well
  • Your team is not yet fluent in Server Components and the migration would introduce risk
  • You have extensive test coverage tied to getServerSideProps patterns that would need to be rewritten

Do not migrate if:

  • The Pages Router is working correctly and you have no specific reason to change
  • Your team is shipping features and a migration would cause prolonged disruption without a clear return

The App Router is demonstrably better for new projects. For existing applications, the cost-benefit calculation depends on size, team capacity, and what you actually need that the Pages Router cannot provide.

What Stays the Same

Not everything changed. Several things carry over:

  • next.config.js configuration (with some additions)
  • The Image and Link components work the same way
  • Environment variable conventions are unchanged
  • Deployment and hosting work identically
  • TypeScript support and the overall DX remain strong

The migration is architectural, not infrastructural. Your deployment pipeline, CI configuration, and hosting setup do not need to change.


The App Router is where Next.js is going. Whether to move now, move incrementally, or wait depends on your project's specific situation — but understanding the architectural difference is the starting point for making that decision well.

If you're evaluating a Next.js migration and want an experienced engineering team to scope it and execute it without disrupting your roadmap, talk to Clixo. We help product teams make these transitions cleanly.