Next.js 13 introduced the App Router as an alternative to the Pages Router. By 2024, the App Router is the default and recommended approach. Here is what actually changed and how to reason about it.
The Mental Model Shift
The Pages Router (next/router, the /pages directory, getServerSideProps / getStaticProps) was a file-system routing system where every page was a React component rendered on the server or at build time. The App Router (/app directory) implements React Server Components (RSC) as the default, fundamentally changing what runs where. In the Pages Router: everything is a Client Component (runs in the browser), unless you explicitly opt out with getServerSideProps/getStaticProps. In the App Router: everything is a Server Component (runs on the server, cannot use hooks, event handlers, or browser APIs) by default; you opt into client rendering by adding ‘use client’ at the top of a file. The key implication: Server Components can directly access databases, file systems, and server secrets (no exposed API keys), render on the server, and send only HTML to the client — reducing JavaScript bundle size.
Layout System
The App Router’s layout system replaces _app.js and _document.js with a nested layout approach. layout.tsx files wrap their children and all nested routes — a layout at /app/dashboard/layout.tsx wraps every page under /dashboard. Layouts persist across navigation (they don’t re-render when you navigate between routes sharing a layout). This is useful for persistent sidebar navigation, authenticated layout wrappers, and shared UI. Loading states: loading.tsx files provide automatic Suspense boundaries — place a loading.tsx next to a page.tsx and Next.js automatically shows the loading UI while the page renders. Error handling: error.tsx files catch errors for their route segment. Template.tsx: like layout.tsx but re-renders on every navigation (for page-level animations or form reset behaviour).
Data Fetching
The old API (getServerSideProps, getStaticProps, getStaticPaths) is entirely replaced by the App Router’s approach. In Server Components, fetch calls with no cache configuration are now cached by default in Next.js — `fetch(url)` is like `getStaticProps`; `fetch(url, {cache: ‘no-store’})` is like `getServerSideProps`. Revalidation: `{next: {revalidate: 60}}` revalidates cached data every 60 seconds (equivalent to ISR in Pages Router). Server Actions: forms can submit to server-side functions directly without an API route (`’use server’` directive) — the form data POST goes to a server function that can write to a database. This significantly reduces boilerplate for form handling. The practical risk: the caching behaviour changed significantly between Next.js versions 13, 14, and 15. Next.js 15 changed fetch caching to opt-in (no caching by default) — check the version you are using, as the default behaviour is not consistent across versions.
Migration Strategy
Pages Router and App Router can coexist in the same Next.js project — /pages routes and /app routes work simultaneously. The recommended migration: start new features in the App Router; migrate existing pages incrementally; don’t rewrite the entire application at once. When not to migrate: if your Pages Router app works well and you have no immediate feature need that requires App Router features, migration is not urgent. The App Router has a genuine learning curve; converting a large Pages Router app is a multi-week project for any non-trivial application.



