What Changed With the App Router

The Next.js App Router, introduced in v13 and stabilised in v14, is a fundamental rethink of how Next.js applications are structured. The Pages Router still works and won't be deprecated, but the App Router is where all active development is happening.

The key changes:

  • File-system routing moves to /app instead of /pages
  • React Server Components (RSC) by default — every component is a Server Component unless you add 'use client'
  • Nested layouts that persist across navigations without re-rendering
  • Streaming HTML with Suspense boundaries for progressively loaded pages
  • A new caching model that can be confusing until you understand its four distinct layers

The mental model shift: With the Pages Router, you thought in terms of pages. With the App Router, you think in terms of a component tree where each node can independently decide whether to render on the server or client.

React Server Components: What They Are and Why They Matter

React Server Components (RSC) render exclusively on the server — their code is never sent to the browser. This means:

  • You can write async components that fetch data directly — no useEffect, no loading states
  • Database queries, file system access, and secret API keys stay on the server
  • The JavaScript bundle sent to the browser is dramatically smaller — only Client Components contribute to it

Example: a Server Component fetching data:

async function ProductList() {
  const products = await db.products.findMany();
  return <ul>{products.map(p => <li>{p.name}</li>)}</ul>;
}

No API route needed. No client-side fetch. The component runs on the server, fetches from your database directly, and sends the rendered HTML to the browser. This is the pattern that makes Next.js App Router sites feel instant.

When to Use Client Components

With RSC as the default, you need 'use client' only when a component needs:

  • Browser APIs (window, localStorage, navigator)
  • Event listeners (onClick, onChange)
  • React hooks that depend on client state (useState, useEffect, useContext)
  • Third-party libraries that aren't RSC-compatible

Best practice: Push Client Components to the leaves of your component tree. Keep layouts, pages, and data-fetching components as Server Components. Only the interactive bits — a modal, a form, a dropdown — need to be Client Components.

Common mistake: Adding 'use client' to an entire page because one small interactive element needs it. Extract the interactive element into its own Client Component and keep everything else as a Server Component.

Understanding the Four-Layer Caching Model

The most confusing part of the App Router for developers coming from the Pages Router is the caching. There are four distinct caches:

1. Request Memoization: Within a single render pass, identical fetch() calls are deduplicated. Call the same endpoint in 10 components — it only hits the network once.

2. Data Cache: Persists fetch() responses across requests. Cached by default. Opt out with {cache: 'no-store'} for real-time data.

3. Full Route Cache: Static pages are cached at build time (SSG). Dynamic pages opt out automatically. Control with export const revalidate = 3600 for ISR.

4. Router Cache: Client-side cache storing prefetched route segments. Reduces navigation time by serving previously-visited routes from memory.

The frustrating scenario: You update your database but the page still shows old data. Check your Data Cache settings — you may need revalidatePath() after mutations or set revalidate = 60 for your page.

Streaming and Suspense: Progressive Page Loading

With the App Router, you can stream HTML progressively to the browser using React Suspense. Instead of waiting for all data to load before sending anything, the server sends the page shell immediately and streams in the dynamic sections as they're ready.

The pattern:

export default function Page() {
  return (
    <div>
      <StaticHeader /> // renders immediately
      <Suspense fallback={<Spinner />}>
        <SlowDataComponent /> // streams in when ready
      </Suspense>
    </div>
  );
}

This makes pages feel faster even when data fetching is slow — users see meaningful content immediately rather than a blank screen or spinner. For e-commerce product pages, this means the product image and title load instantly while reviews and recommendations stream in.

Performance Benchmarks: App Router vs Pages Router

In our own benchmarks across real client projects migrated from Pages to App Router:

  • Time to First Byte (TTFB): 40–60% improvement due to streaming and edge rendering
  • LCP: 25–40% improvement from RSC reducing JavaScript payload
  • Total JS bundle size: 30–50% smaller — only Client Components contribute to the bundle
  • Core Web Vitals INP: Improved by 20–35% due to less main-thread blocking JS

Bottom line: The App Router is the right choice for all new Next.js projects in 2025. The learning curve is real — particularly around caching and the Server/Client Component boundary — but the performance and developer experience benefits are significant and compounding.