CCelestiallines.com
← Back to blog

Next.js App Router vs Pages Router: What You Need to Know

By Pawan Singh · Jun 2026

Two Different Routing Systems, Not Two Versions of the Same One

The Pages Router (pages/) was Next.js's original routing system. The App Router (app/) is a newer, actively-developed system built around React Server Components, streaming, and nested layouts. Both can technically coexist in one project during a migration, but they're architecturally distinct enough that "just move the files" isn't a real migration strategy.

File Conventions Compared

pages/blog/[slug].js       →  app/blog/[slug]/page.tsx
pages/_app.js               →  app/layout.tsx
pages/api/users.js          →  app/api/users/route.ts
pages/404.js                 →  app/not-found.tsx

The App Router's convention is a folder per route segment with a special page.tsx file inside it, rather than the file itself being the route — this is what enables colocating other special files (layout.tsx, loading.tsx, error.tsx) alongside a route without them becoming routes themselves.

Server Components by Default

The biggest actual behavior change: components in app/ are React Server Components by default, rendering only on the server and never shipping their code to the client, unless explicitly marked 'use client'. The Pages Router has no equivalent concept — every component there is effectively a client component that also happens to render on the server for the initial load, with its full code always bundled for the browser.

Nested Layouts That Persist Across Navigation

// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
    return (
        <div>
            <Sidebar />
            {children}
        </div>
    );
}

A layout wraps every route beneath it in the folder tree and, critically, doesn't re-render or remount on navigation between those routes — the sidebar in this example stays mounted (preserving its own state, like a scroll position or an open/closed toggle) as the user navigates between different pages under /dashboard. The Pages Router's _app.js is a single, flat, application-wide wrapper with no equivalent per-section nesting.

Data Fetching: A Fundamentally Different Model

Pages Router: getServerSideProps, getStaticProps, and getStaticPaths are special exported functions Next.js calls for you, each with specific timing and caching semantics you choose per page.

// Pages Router
export async function getServerSideProps() {
    const data = await fetchData();
    return { props: { data } };
}

App Router: Server Components just fetch() or query directly, with async/await, no special exported function required:

// App Router
async function Page() {
    const data = await fetchData();
    return <div>{data.title}</div>;
}

Caching behavior in the App Router is controlled by fetch options ({ cache: 'force-cache' } for static-like behavior, { cache: 'no-store' } for always-fresh, or { next: { revalidate: 60 } } for time-based revalidation) rather than by choosing between entirely different exported function names.

Streaming and Loading States

The App Router supports streaming server-rendered HTML in pieces via loading.tsx and <Suspense> boundaries — a slow-loading section of a page doesn't block the rest of the page from appearing:

// app/dashboard/loading.tsx — shown automatically while the page's
// data-fetching is still in progress
export default function Loading() {
    return <Spinner />;
}

The Pages Router has no built-in equivalent — getServerSideProps blocks the entire response until it resolves, so a single slow query delays the whole page rather than just its own section.

Should You Migrate an Existing Pages Router App?

Next.js supports incremental migration — both routers can coexist, migrating one route at a time. Whether it's worth doing depends on the project: an actively developed app benefits from Server Components, streaming, and nested layouts enough to justify the migration effort over time; a stable, rarely-touched app with no pressing performance or DX complaint may not need the disruption. New projects should start directly on the App Router — it's where Next.js's own development focus and new features are concentrated.

Common Migration Pitfalls

  • Marking everything 'use client' out of habit — this defeats most of the App Router's actual benefit; only components that genuinely need interactivity, state, or browser APIs should opt out of being a Server Component.
  • Expecting getServerSideProps-style functions to still work in app/ — they don't; data fetching is a genuinely different model, not a renamed version of the same one.
  • Forgetting that layouts persist across navigation — state intentionally kept in a layout component survives route changes beneath it, which is a feature, but surprises anyone expecting Pages Router's "everything remounts on navigation" behavior.

Migrating a Next.js app to the App Router, or starting a new one? Get in touch — I work as a freelance Next.js developer.

Comments