DEV Community

Learcise
Learcise

Posted on

Next.js App Router Deep Dive: Evolution from Pages Router and Its Latest Features

Introduced as stable in Next.js 13.4, the App Router represents a major leap forward compared to the traditional Pages Router. Built on React Server Components (RSC), this new architecture enhances performance, enables more complex UI patterns, and completely refreshes the developer experience.

In this article, we’ll cover a comparison between Pages Router and App Router, file conventions, Suspense and Streaming, Parallel Routes and Intercepting Routes, the <Link> component, and middleware—all the practical points you should know.


1. Comparison: Pages Router vs App Router

Let’s start by comparing the legacy Pages Router with the new App Router to get the big picture.

Feature Pages Router App Router
Directory pages/ app/
Component Type Client Components by default Server Components by default (use client to switch)
Data Fetching getStaticProps / getServerSideProps Direct fetch calls on the server
Layouts _app.tsx / _document.tsx layout.tsx (nested layouts supported)
Routing Flat, file-based Folder hierarchy-based, supports Route Groups
Delayed UI Manual implementation Suspense + loading.tsx + Streaming
Error Handling _error.tsx / 404.tsx error.tsx / not-found.tsx
API Routes pages/api/*.ts route.ts
Complex UI / Modals Implemented with state logic and conditions Officially supported with Parallel Routes / Intercepting Routes
SEO / Metadata next/head metadata API with type safety

👉 App Router evolves toward clear separation of responsibilities and official support for complex UI patterns.


2. App Router File Conventions

In App Router, placing special files inside the app/ directory defines routing and UI behavior.

File Purpose
page.tsx The main page component, mapped to the URL.
layout.tsx Shared layout. Nested and persistent across page transitions.
template.tsx Similar to layout.tsx but re-mounts on navigation (e.g., resets forms).
loading.tsx Loading UI during data fetching. Works with Suspense automatically.
error.tsx Error boundary for the route.
not-found.tsx Custom 404 UI for the route.
route.ts Defines API routes by exporting functions for HTTP methods.
default.tsx Default UI for Parallel Routes when nothing is selected.
(group) directory Grouping for organizational purposes without affecting the URL.
[param] directory Dynamic routes, e.g., /users/[id].

👉 The ability to separate loading, error, and not-found handling per route is a huge step forward in clarity and maintainability.


3. Suspense and Streaming

Suspense

<Suspense> displays a fallback UI while data is being fetched or components are being lazily loaded.

<Suspense fallback={<p>Loading comments...</p>}>
  <Comments />
</Suspense>

Enter fullscreen mode Exit fullscreen mode

👉 While fetching, it shows “Loading comments...,” then seamlessly swaps in the final UI when ready.

Streaming

Streaming sends HTML to the client as soon as each part is ready, rather than waiting for the entire page.

This eliminates the dreaded “blank screen wait,” leading to a much smoother UX.


4. Parallel Routes and Intercepting Routes

Parallel Routes

Allows rendering multiple routes simultaneously.

Example: In an email app, render “inbox” and “details” side by side.

// layout.tsx
export default function Layout({ inbox, detail }: { inbox: React.ReactNode, detail: React.ReactNode }) {
  return (
    <div className="grid grid-cols-2">
      <div>{inbox}</div>
      <div>{detail}</div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Intercepting Routes

Intercepts navigation and replaces it with an alternative view, such as a modal.

Example: Show a product detail page inside a modal when clicked.

👉 This combines SPA-style modal navigation with full-page rendering when accessed directly.


5. <Link> Component and Prefetching

The <Link> component enables fast client-side navigation in Next.js.

How It Works

  1. When a <Link> enters the viewport, Next.js prefetches the resources for the target page.
  2. On click, the preloaded resources are used instantly.
  3. Page navigation completes nearly instantly.

👉 Thanks to prefetching, navigation feels like instant page switching.


6. Middleware

Overview

middleware.ts runs immediately after a request reaches the server, before rendering a page or API route.

Common Use Cases

  • Authentication check: Redirect unauthenticated users to /login.
  • Locale detection: Redirect based on Accept-Language headers.
  • A/B testing: Split traffic between variants.
  • Access control: Block requests based on IP or user-agent.

Example

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("token");

  if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};

Enter fullscreen mode Exit fullscreen mode

👉 This lets you enforce authentication and redirects before any page logic runs.


Conclusion

  • App Router advancements
    • Nested layouts, clearer file conventions, and Streaming/Suspense for improved UX.
    • Complex UI officially supported with Parallel Routes and Intercepting Routes.
  • <Link> advantages
    • Prefetching enables instant-feeling client-side navigation.
  • Middleware power
    • Perform auth checks, redirects, and locale detection right after the request arrives.

👉 For new projects, App Router is strongly recommended. For existing projects, migrate step by step to avoid risks.


References

Top comments (0)