ESC

Type to search the knowledge base.

Next.js App Router Overview

app/ directory mental model — layouts, server components by default, routing conventions, and how App Router differs from Pages Router.

beginner5 min read
  • nextjs
  • app-router
  • routing
  • rsc

The App Router (app/) is Next.js’s modern routing and rendering model. It is built around React Server Components (RSC), nested layouts, and file-system conventions for loading/error UI, routes, and metadata. The older Pages Router (pages/) still works; new features and docs center on App Router.

Docs: Next.js App Router, Routing, React Server Components.

Mental model in one screen

app/
  layout.tsx          ← root layout (html/body), wraps all routes
  page.tsx            ← UI for /
  loading.tsx         ← Suspense fallback for the segment
  error.tsx           ← error boundary for the segment
  not-found.tsx
  blog/
    layout.tsx        ← nested layout, wraps /blog/*
    page.tsx          ← /blog
    [slug]/
      page.tsx        ← /blog/:slug
  api/
    hello/
      route.ts        ← Route Handler (HTTP endpoint)
  • Folders define URL segments.
  • Special files define UI and behavior for that segment.
  • Layouts persist across navigations of their children (state/UI chrome can remain mounted).
  • Default components are Server Components — no "use client" unless you need browser APIs, state, or effects.

App Router vs Pages Router

App Router Pages Router
Directory app/ pages/
Components default Server Components Client (classic React)
Layouts Nested layout.tsx _app + limited patterns
Data fetching async Server Components, fetch cache model, Server Actions getServerSideProps / getStaticProps
Routing extras Intercepting, parallel routes, route groups Conventional file routes
API route.ts handlers pages/api/*

You can migrate incrementally; both trees can coexist. Don’t mix mental models inside one feature without knowing where the boundary is.

Core file conventions

File Role
layout.tsx Shared UI shell; receives children
page.tsx The leaf UI that makes a route publicly accessible
loading.tsx Instant loading UI via Suspense
error.tsx Client error boundary for the segment
template.tsx Like layout but remounts on navigation
default.tsx Fallback for parallel routes
route.ts HTTP handler (GET/POST/…)
middleware.ts Edge middleware (project root / src)

A route is not complete without a page.js/ts (or a route handler for APIs). Layouts alone don’t create a URL.

Route groups and organization

app/
  (marketing)/
    page.tsx          → /
    about/page.tsx    → /about
  (shop)/
    layout.tsx
    cart/page.tsx     → /cart

Parentheses (group) organize code without affecting the URL. Use them for different root layouts (marketing vs app chrome) or team boundaries.

Dynamic segments

app/blog/[slug]/page.tsx
app/shop/[category]/[item]/page.tsx
app/docs/[...slug]/page.tsx      # catch-all
app/docs/[[...slug]]/page.tsx    # optional catch-all
// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPost(slug);
  return <article>…</article>;
}

(Next.js versions tightened params / searchParams toward async — check your version’s docs and type helpers.)

Server Components by default

// app/users/page.tsx — Server Component
import { db } from '@/lib/db';

export default async function UsersPage() {
  const users = await db.user.findMany();
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

Benefits aimed at real product metrics:

  1. Less client JS for data-heavy pages → better INP and parse/hydrate cost.
  2. Secrets and DB stay on the server.
  3. Streaming with Suspense for progressive HTML (critical path / LCP when done well).

When you need state, effects, or browser-only APIs:

'use client';

import { useState } from 'react';

export function Counter() {
  const [n, setN] = useState(0);
  return <button type="button" onClick={() => setN(n + 1)}>{n}</button>;
}

Push "use client" to the leaves. A client parent forces its imports into the client bundle boundary. Deep dive: Server Components in Next.js.

import Link from 'next/link';

<Link href="/blog/hello">Hello</Link>

Prefer Link / next/navigation over raw <a> for internal routes so client transitions and prefetch work. Still: links navigate, buttons act — links vs buttons.

Prefetching is on for static routes in viewport by default — know that for analytics and bursty dashboards.

Data and caching (high level)

App Router fetch integrates with Next’s cache/revalidate model:

const res = await fetch('https://api.example.com/item', {
  next: { revalidate: 60, tags: ['item'] },
});

Mental buckets: static vs dynamic rendering, revalidatePath / revalidateTag, and when cookies/headers force dynamic. Treat this as its own topic — overview stop: caching is opt-in complexity; measure TTFB and LCP when you change it.

Metadata

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Blog',
  description: 'Posts from the team',
};

Or generateMetadata for dynamic SEO. Replaces many next/head patterns from Pages Router.

Security and boundaries

  • Server Components can import server-only modules; don’t leak secrets into client components via props that serialize to the client.
  • Server Actions need authz checks — they are public endpoints.
  • CSP nonces integrate at the document layer — see Content Security Policy.
  • User HTML still XSS — XSS guide.

Common pitfalls

  1. Marking entire trees "use client" “to make hooks work” — push the boundary down.
  2. Fetching in client components what a Server Component could load cheaper.
  3. Forgetting root layout.tsx must include <html> and <body>.
  4. Expecting layout state to reset on every navigation — use template if you need remounts.
  5. Mixing pages/ data APIs inside app/ files.
  6. Over-prefetching authenticated pages with sensitive data assumptions.

Interview angle

Explain nested layouts + Server Components default. Contrast App vs Pages data fetching. Show when "use client" is required. Mention loading.tsx as segment Suspense and impact on UX/streaming.

One-liner:

“App Router is file-system routes with nested layouts and RSC by default; client components are opt-in leaves.”

Further reading

Related guides