vom Pages Router: Der vollständige Leitfaden
Der App Router ist nicht nur ein neues Verzeichnis – er ändert das mentale Modell von Next.js fundamental. Layouts, Server Components, verschachtelte Routen und das neue Data-Fetching-Paradigma erfordern einen durchdachten Migrationsplan, der Produktionsausfälle vermeidet und das Team nicht überfordert.
Inhaltsverzeichnis
- 1. Warum der App Router mehr ist als ein Refactor
- 2. Neue Verzeichnisstruktur und Routing-Konventionen
- 3. React Server Components verstehen und einsetzen
- 4. Layouts: gemeinsame UI ohne Re-Render
- 5. Data Fetching: von getServerSideProps zu async Components
- 6. Route Handlers ersetzen API Routes
- 7. Client Components: use client gezielt einsetzen
- 8. Inkrementelle Migrationsstrategie
- 9. Pages Router vs. App Router im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum der App Router mehr ist als ein Refactor
Der Next.js App Router ist seit Version 13 stabil und seit Version 14 die empfohlene Standard-Architektur. Wer die Migration vom Pages Router als simples Verschieben von Dateien betrachtet, wird schnell auf Widerstand stoßen: Das mentale Modell dahinter ist fundamental verschieden. Statt dem klassischen Request-Response-Zyklus mit getServerSideProps oder getStaticProps nutzt der App Router React Server Components als erste Abstraktion, die standardmäßig serverseitig gerendert werden und keinen Client-Bundle beladen.
Das Ergebnis ist eine deutlich verbesserte Core Web Vitals-Performance: Da Server Components kein JavaScript an den Browser senden, schrumpft der initiale Bundle erheblich. Gleichzeitig ermöglicht die neue Layouts-Architektur, dass nur die sich verändernden Segmente einer Seite neu gerendert werden – Navigationen, Sidebars und Header bleiben im Speicher und werden nicht bei jedem Route-Wechsel neu gemountet. Die App Router Migration lohnt sich für fast jedes bestehende Next.js-Projekt, erfordert aber einen strukturierten Plan.
Der wichtigste Schritt vor der Migration: Verstehen, welche Teile der Anwendung echte Interaktivität benötigen (Browser-Events, useState, useEffect) und welche rein darstellend sind. Darstellende Komponenten werden zu Server Components und gewinnen dabei Direktzugriff auf Datenbanken, Dateisystem und Secrets – ohne dass ein separater API-Endpunkt notwendig wäre. Interaktive Teile werden mit der 'use client'-Direktive markiert und bleiben Client Components.
2. Neue Verzeichnisstruktur und Routing-Konventionen
Im App Router ersetzt das app/-Verzeichnis das bisherige pages/-Verzeichnis. Beide können während der Migration koexistieren – Next.js erkennt sie und behandelt sie als separate Router. Routes werden durch Ordner definiert, nicht durch Dateien. Eine Seite braucht eine page.tsx-Datei im entsprechenden Ordner, ein Layout eine layout.tsx. Spezielle Dateien wie loading.tsx, error.tsx, not-found.tsx und template.tsx greifen automatisch für das jeweilige Route-Segment.
Dynamische Segmente funktionieren weiterhin mit eckigen Klammern ([slug]), aber Catch-All-Segmente ([...slug]) und optionale Catch-All-Segmente ([[...slug]]) verhalten sich identisch wie im Pages Router. Neu sind Route Groups mit Klammern: (marketing)/about/page.tsx und (shop)/about/page.tsx erzeugen beide die Route /about, haben aber getrennte Layouts und Middleware-Scopes. Das ist besonders nützlich, wenn verschiedene Bereiche einer Anwendung komplett unterschiedliche Shell-Layouts brauchen.
// app/layout.tsx — Root layout replaces pages/_app.tsx and pages/_document.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: { template: '%s | Mironsoft', default: 'Mironsoft' },
description: 'Next.js App Router example',
}
// Root layout MUST return html and body elements
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="de">
<body className="bg-white text-gray-900 antialiased">
<header className="sticky top-0 z-50 border-b bg-white/80 backdrop-blur">
<nav className="mx-auto max-w-7xl px-4 py-4">Navigation</nav>
</header>
<main>{children}</main>
<footer className="border-t py-8">Footer</footer>
</body>
</html>
)
}
// app/(shop)/layout.tsx — Nested layout for the shop segment
export default function ShopLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex gap-8">
<aside className="w-64 shrink-0">Sidebar</aside>
<div className="flex-1">{children}</div>
</div>
)
}
3. React Server Components verstehen und einsetzen
Im App Router sind alle Komponenten standardmäßig Server Components. Das bedeutet: Sie laufen ausschließlich auf dem Server, haben Zugriff auf Node.js-APIs, können direkt mit der Datenbank kommunizieren und senden kein JavaScript an den Browser. Das ist eine grundlegende Verschiebung gegenüber dem Pages Router, wo alle Komponenten Client-seitig ausgeführt wurden und Daten nur über spezielle Props-Funktionen (getServerSideProps, getStaticProps) erhalten konnten.
Server Components können async sein und direkt auf Datenbankabfragen oder externe APIs warten – ohne useEffect oder useState. Das eliminiert den häufigen Rendering-Zyklus: Komponente rendert leer, Daten werden geladen, Komponente rendert erneut mit Daten. Stattdessen wartet der Server auf alle Daten und sendet vollständig gerenderte HTML an den Browser. Das verbessert die wahrgenommene Performance erheblich, besonders auf langsamen Verbindungen. Fetch-Requests in Server Components werden von Next.js automatisch dedupliziert und gecacht.
Die wichtigste Einschränkung: Server Components dürfen keine Browser-APIs verwenden, keinen State halten und keine Event-Handler registrieren. Wenn eine Komponente onClick, useState oder useEffect braucht, muss sie eine Client Component sein. Das Kompositionsmuster ist dabei entscheidend: Eine Server Component kann eine Client Component als Child importieren und rendern – aber nicht umgekehrt. Daten werden als Props von der Server Component zur Client Component weitergegeben.
4. Layouts: gemeinsame UI ohne Re-Render
Das Layouts-System des App Routers löst eines der hartnäckigsten Probleme im Pages Router: Gemeinsame UI-Elemente wie Navigation, Sidebar oder Footer wurden bei jedem Seitenwechsel neu gemountet und gerendert, weil Next.js die gesamte Seite austauschte. Im App Router bleiben Layouts beim Navigieren innerhalb ihres Segments stabil im DOM – nur die page.tsx-Komponente darunter wird ersetzt. Das bedeutet: Keine verlorenen Scroll-Positionen in der Sidebar, keine re-triggerten Animationen in der Navigation, kein flackernder Header.
Layouts lassen sich beliebig tief schachteln. Das Root Layout wrapping die gesamte Anwendung, ein Shop-Layout wrapping alle Shop-Seiten, ein Produkt-Layout wrapping alle Produktseiten. Jedes Layout erhält sein Segment-spezifisches children-Prop und kann eigene Daten laden, ohne dass diese Daten an alle Kindseiten als Props durchgereicht werden müssen. Das führt zu einer saubereren Komponentenhierarchie, in der jede Schicht nur die Daten kennt, die sie wirklich braucht.