Next.js Advanced Routing: Route Groups, Nested Layouts, Parallel Routes und Intercepting Routes

Letzte Woche habe ich ein zwei Jahre altes Next.js-E-Commerce-Projekt übernommen. Beim Öffnen von app starrte ich auf über 60 Ordner – about, products, admin-users, marketing-campaign, shop-cart … alles flach nebeneinander. Eine benutzerbezogene Seite finden? Durch Marketing-Ordner wühlen. Noch frustrierender: drei Leute ändern gleichzeitig Routen – mindestens zwei Git-Konflikte pro Tag; im Code-Review brauchte es oft eine halbe Stunde nur, um die Dateistruktur zu verstehen.
Dann fiel mir ein: Next.js hat Route Groups, Parallel Routes und mehr – seit Next.js 13 dokumentiert, im Projekt aber ungenutzt. Nicht, weil die Tools fehlen, sondern weil unklar war, wann welches Feature passt.
Drei Stunden Dokumentation, zwei Tage Refactoring: die Struktur war aufgeräumt, im Team-Chat kein „wieder Konflikt“. Die vier Features lösen konkrete Probleme: Route Groups ordnen Verzeichnisse, Nested Layouts nutzen Struktur wieder, Parallel Routes zeigen mehrere Bereiche gleichzeitig, Intercepting Routes ermöglichen elegante Modals.
Wenn Ihr Next.js-Projekt wächst, Dateien unübersichtlich werden und das Team ständig kollidiert – nach diesem Artikel wissen Sie, welches Feature wann wie einzusetzen ist und welche Fallen Sie vermeiden sollten.
Route Groups – aufgeräumte Verzeichnisse
Was sind Route Groups?
Kurz: Ordner in Klammern, z. B. (marketing) oder (shop). Next.js setzt den Klammernamen nicht in die URL.
Ohne reales Szenario wirkt das banal. Stellen Sie sich vor: Marketing (Start, Über uns), Shop (Produkte, Warenkorb), Admin (Bestellungen, Nutzer). Klassisch: alles unter app oder erzwungene URL-Präfixe wie /marketing/about – unattraktiv.
Route Groups trennen im Dateisystem, ohne die sichtbare URL zu verändern.
Drei praktische Einsatzzwecke
1. Nach Team oder Funktion ordnen
60 flache Ordner werden drei Gruppen:
app/
├── (marketing)/ # Marketing-Team
│ ├── page.js # Start → yoursite.com/
│ ├── about/ # → yoursite.com/about
│ └── pricing/ # → yoursite.com/pricing
├── (shop)/ # Frontend-Team
│ ├── products/ # → yoursite.com/products
│ └── cart/ # → yoursite.com/cart
└── (dashboard)/ # Backend-Team
├── orders/ # → yoursite.com/orders
└── users/ # → yoursite.com/users
URLs bleiben sauber; die Struktur ist auf einen Blick klar.
2. Unterschiedliche Root-Layouts pro Bereich
Marketing und Admin brauchen verschiedene Navigation – beide starten aber unter /about bzw. /orders. Lösung: pro Route Group ein layout.js.
app/
├── (marketing)/
│ ├── layout.js # Marketing: Top-Nav + Hero
│ └── ...
├── (shop)/
│ ├── layout.js # Shop: Warenkorb-Icon + Kategorien
│ └── ...
└── (dashboard)/
├── layout.js # Admin: Sidebar + Auth-Check
└── ...
Drei Layouts, ein Projekt – ohne Subdomains oder mehrere Next.js-Instanzen.
3. Layout selektiv teilen
Manche Seiten teilen ein Layout, andere nicht. Beispiel Blog: Artikel mit linker Navigation, Startseite ohne.
app/
├── blog/
│ ├── page.js # Blog-Start ohne Sidebar
│ └── (articles)/ # Gruppe mit Sidebar-Layout
│ ├── layout.js
│ ├── [slug]/ # → /blog/xxx
│ └── ...
(articles) ändert die URL nicht – /blog/my-first-post bleibt, nur diese Gruppe nutzt das Sidebar-Layout.
Refactoring-Beispiel
Vorher (Auszug):
app/
├── page.js
├── about/
├── pricing/
├── products/
├── products-detail/
├── cart/
├── checkout/
├── admin-orders/
├── admin-users/
├── admin-settings/
├── marketing-campaign/
├── ...(noch ~50)
Nachher:
app/
├── (marketing)/
│ ├── layout.js
│ ├── page.js
│ ├── about/
│ ├── pricing/
│ └── campaign/
├── (shop)/
│ ├── layout.js
│ ├── products/
│ ├── cart/
│ └── checkout/
└── (dashboard)/
├── layout.js
├── orders/
├── users/
└── settings/
Konflikte im Review sanken um etwa 70 %, weil Teams nur „ihre“ Group anfassen.
Drei Fallstricke
1. URL-Konflikte
app/
├── (marketing)/
│ └── about/page.js # → /about
└── (shop)/
└── about/page.js # → /about (Konflikt!)
Next.js meldet Error: Conflicting route. Lösung: Pfad umbenennen oder echte Verzeichnisebene ohne Klammern.
2. Vollständiger Reload zwischen Root-Layouts
Wechsel von (shop) nach (marketing) kann kurz „flackern“ – verschiedene Root-Layouts werden entladen und neu gemountet (full page load). Gemeinsame Teile in app/layout.js auslagern, Unterschiede nur in Group-Layouts.
3. Startseite bei mehreren Root-Layouts
Mit mehreren Groups und je eigenem layout.js gehört page.js der Startseite in eine Group (z. B. (marketing)/page.js), nicht nach app/page.js.
Kern: Route Groups organisieren Dateien, ändern URLs nicht und erlauben getrennte Root-Layouts. Ab ~20 Ordner unter app lohnt sich der Schritt.
Nested Layouts – flexible UI-Hierarchie
Welches Problem lösen sie?
Startseite: Top-Nav. Blog-Liste: Nav + linke Kategorien. Artikel: Nav + Kategorien + rechtes Inhaltsverzeichnis.
Ohne Nested Layouts bauen Sie das in jeder Seite manuell zusammen – eine Nav-Änderung betrifft viele Dateien.
Nested Layouts verschachteln Layouts wie Matroschka-Puppen – jede Ebene fügt UI hinzu, ohne äußere Ebenen zu wiederholen.
Funktionsweise
Jeder Ordner kann layout.js haben; Kinder erben und erweitern.
Beispiel Lernplattform:
app/
├── layout.js # Root: Header + Footer
└── courses/
├── layout.js # + linke Kurskategorien
├── page.js
└── [id]/
├── layout.js # + Fortschrittsleiste rechts
└── page.js
Für /courses/123:
app/layout.js(Header + Footer)courses/layout.js(Sidebar)courses/[id]/layout.js(Fortschritt)courses/[id]/page.js(Inhalt)
Nav-Änderung nur in app/layout.js – gilt überall.
Blog mit drei Ebenen
app/
├── layout.js # Header + Footer
└── blog/
├── layout.js # + Sidebar
├── page.js
└── [slug]/
├── layout.js # + Inhaltsverzeichnis
└── page.js
app/layout.js
export default function RootLayout({ children }) {
return (
<html>
<body>
<Header />
{children}
<Footer />
</body>
</html>
)
}
app/blog/layout.js
export default function BlogLayout({ children }) {
return (
<div className="blog-container">
<Sidebar />
<main>{children}</main>
</div>
)
}
app/blog/[slug]/layout.js
export default function ArticleLayout({ children }) {
return (
<div className="article-container">
{children}
<TableOfContents />
</div>
)
}
Jedes Layout kümmert sich nur um seine Ebene – Next.js verschachtelt automatisch.
Mit Route Groups kombinieren
Route Groups trennen horizontal (Marketing / Shop / Admin), Nested Layouts stapeln vertikal.
app/
└── (shop)/
├── layout.js # Shop-Root: Warenkorb + Kategorien
├── products/
│ ├── layout.js # + Filter-Sidebar
│ ├── page.js
│ └── [id]/
│ ├── layout.js # + Breadcrumb
│ └── page.js
└── cart/
└── page.js
/products: Shop-Root + Filter/products/123: alle drei Ebenen/cart: nur Shop-Root
Keine if (detailPage)-Logik in einer Mega-Komponente.
Zwei Details
1. Layouts rendern beim Unterrouten-Wechsel nicht neu
Von /blog nach /blog/my-post: app/layout.js und blog/layout.js bleiben – Scroll und Eingaben in der Sidebar bleiben erhalten.
2. Layout sieht keine dynamischen params der Kind-Route
params.id aus products/[id]/page.js ist in layout.js nicht verfügbar. Titel im Layout? Context oder Daten eine Ebene höher.
Kern: Verzeichnistiefe = UI-Tiefe – ein layout.js pro Ebene, eine Änderung für eine ganze Region.
Parallel Routes – mehrere Seiten gleichzeitig
Wann brauchen Sie sie?
Dashboard mit drei Modulen: Verkaufschart (langsam), Bestellliste (schnell), Lagerwarnung (andere API). Klassisch: drei Requests in einer page.js – ein Fehler killt alles, ein Loading-State für alle.
Parallel Routes rendern mehrere unabhängige Slots in einer Ansicht – je Slot eigenes loading.js und error.js, paralleles Laden.
Syntax: @ für Slots
app/
└── dashboard/
├── layout.js
├── @sales/
│ └── page.js
├── @orders/
│ └── page.js
├── @inventory/
│ └── page.js
└── page.js
Ordner mit @ sind Slots. Im Layout kommen sie als Props:
export default function DashboardLayout({
children,
sales,
orders,
inventory
}) {
return (
<div className="dashboard">
<div className="widgets">
<div className="widget">{sales}</div>
<div className="widget">{orders}</div>
</div>
<div className="main">{children}</div>
<div className="alerts">{inventory}</div>
</div>
)
}
Dashboard-Beispiel
@sales/page.js
async function getSalesData() {
const res = await fetch('https://api.example.com/sales')
return res.json()
}
export default async function SalesWidget() {
const data = await getSalesData()
return (
<div>
<h3>Umsatz diesen Monat</h3>
<Chart data={data} />
</div>
)
}
@orders/page.js und @inventory/page.js analog – Slots laden parallel. Schnelle Module erscheinen zuerst; ein Fehler betrifft nur einen Slot.
Eigenes loading.js und error.js
app/
└── dashboard/
├── @sales/
│ ├── page.js
│ ├── loading.js
│ └── error.js
└── ...
@sales/loading.js
export default function SalesLoading() {
return <div>Verkaufsdaten werden geladen …</div>
}
@sales/error.js
'use client'
export default function SalesError({ error, reset }) {
return (
<div>
<p>Verkaufsdaten konnten nicht geladen werden</p>
<button onClick={reset}>Erneut versuchen</button>
</div>
)
}
default.js
Navigation von /dashboard nach /dashboard/settings: Slots passen nicht mehr – Standard: alter Slot-Inhalt bleibt. Mit default.js und return null verschwindet der Slot beim Wechsel.
Parallel Routes + Intercepting Routes = typisches Modal-Muster (nächstes Kapitel). Allein schon für Dashboards mit unabhängigen Modulen sehr nützlich.
Intercepting Routes – elegante Modals
Instagram-ähnliche Bildansicht
Feed → Bild klicken → Modal, URL /photo/abc123. Zurück schließt Modal, Refresh zeigt Vollseite, geteilte URL öffnet Vollseite.
Intercepting Routes fangen Client-Navigation ab und zeigen Zielinhalt als Modal; bei direktem Aufruf oder Refresh die normale Vollseite.
Syntax
(.)– gleichstufige Route(..)– eine Ebene höher(..)(..)– zwei Ebenen höher(...)– von Root
Liste /photos, Klick auf /photos/123 → Modal bei Navigation, Vollseite bei Refresh:
app/
├── @modal/
│ ├── (.)photos/
│ │ └── [id]/
│ │ └── page.js
│ └── default.js
├── layout.js
├── page.js
└── photos/
└── [id]/
└── page.js
@modal/(.)photos/ fängt die gleichstufige photos-Route ab.
Schritt für Schritt
app/layout.js
export default function RootLayout({ children, modal }) {
return (
<html>
<body>
{children}
{modal}
</body>
</html>
)
}
app/page.js – Grid mit Links zu /photos/${id}.
app/@modal/(.)photos/[id]/page.js
'use client'
import { useRouter } from 'next/navigation'
import Image from 'next/image'
export default function PhotoModal({ params }) {
const router = useRouter()
return (
<div className="modal-backdrop" onClick={() => router.back()}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<button onClick={() => router.back()}>Schließen</button>
<Image src={`/images/photo${params.id}.jpg`} fill />
</div>
</div>
)
}
app/photos/[id]/page.js – Vollseite mit Nav und Beschreibung.
app/@modal/default.js
export default function Default() {
return null
}
Verhalten: Klick → Client-Nav → Intercept → Modal über Feed; Zurück → default.js → Modal weg; Refresh/Direktaufruf → photos/[id]/page.js ohne Modal.
Ebene wählen
Intercept unter app/@modal/:
- Ziel
app/photos/→(.)photos - Ziel
app/shop/products/vonshop/@modal→(..)products - Unsicher →
(...)von Root, später verfeinern
Fallstricke
- Ohne
default.jsbleibt Modal sichtbar. useRouterbraucht'use client'im Modal.- Tiefe Verschachtelung: Pfad genau berechnen oder vorerst
(...).
Intercepting + Parallel Routes: teilbare URL, Refresh = Vollseite, Zurück schließt Modal – wie bei Instagram, Twitter, Airbnb.
Kombiniert – alle vier Features
E-Commerce-Struktur
Drei Bereiche: Marketing (/, /about), Shop (/products, /cart), Dashboard (/dashboard).
Shop: Listen-Filter, Detail-Breadcrumb, Schnellvorschau-Modal (Intercept), Refresh = Voll-Detail.
Dashboard: @sales, @orders, @inventory parallel.
app/
├── layout.js
├── (marketing)/
│ ├── layout.js
│ ├── page.js
│ ├── about/
│ └── pricing/
├── (shop)/
│ ├── layout.js
│ ├── @modal/
│ │ ├── (.)products/
│ │ │ └── [id]/page.js
│ │ └── default.js
│ ├── products/
│ │ ├── layout.js
│ │ ├── page.js
│ │ └── [id]/
│ │ ├── layout.js
│ │ └── page.js
│ └── cart/page.js
└── (dashboard)/
├── layout.js
├── @sales/ ...
├── @orders/ ...
├── @inventory/ ...
└── page.js
app/(shop)/layout.js – {children} und {modal}.
app/(shop)/@modal/(.)products/[id]/page.js – Client-Modal mit router.back() und Link zur Voll-Detailseite.
app/(dashboard)/layout.js – {sales}, {orders}, {inventory} parallel.
Warum so?
| Entscheidung | Grund |
|---|---|
| Route Groups | Getrennte Root-Layouts, weniger Team-Konflikte |
| Nested Layouts | UI-Stufen = Ordnerstufen |
| Intercept + Parallel | Vorschau ohne Listenverlust, URL trotzdem /products/123 |
| Parallel im Dashboard | Unabhängige Lade- und Fehlerzustände |
Nach dem Refactoring: ~65 % weniger Konflikte, schnelleres Onboarding, gezielte Layout-Änderungen, höhere Conversion bei Schnellvorschau (Nutzer mussten seltener zurücknavigieren).
Fazit
Vier Features im Überblick:
| Feature | Rolle | Szenario | Syntax |
|---|---|---|---|
| Route Groups | Dateien ordnen, Layouts trennen | Multi-Bereich, Teams | (name) |
| Nested Layouts | UI schichten | Mehrstufige Navigation | layout.js pro Ebene |
| Parallel Routes | Mehrere Fragmente | Dashboard, Module | @slot |
| Intercepting Routes | Nav abfangen, Modal | Feed + Detail-URL | (.) (..) (...) |
Empfehlung: Nicht alles auf einmal. Zuerst Route Groups; Nested Layouts bei gestaffelter UI; Parallel und Intercept für Dashboard und Modals.
Am Anfang wirkt die Doku dicht – nach einmaligem Einsatz passt es: Verzeichnis = Route, Tiefe = UI, Intercept = UX.
Wenn app unübersichtlich wird: einen halben Tag in Route Groups investieren – in drei Monaten danken Sie sich oft selbst dafür.
Vollständiger Refactoring-Workflow für Next.js Advanced Routing
Von chaotischer Verzeichnisstruktur zu Route Groups, Nested Layouts, Parallel Routes und Intercepting Routes
⏱️ Estimated time: 4 hr
- 1
Step 1: Bestehende Projektstruktur analysieren
Komplexität der aktuellen Routen bewerten:
• Ordner unter app zählen (ab 20 empfiehlt sich Route Groups)
• Funktionsbereiche erkennen (Marketing, Shop, Admin)
• Seitengruppen mit unterschiedlichen Layouts identifizieren
• Konfliktpunkte im Team dokumentieren
Entscheidungskriterien:
• Ordner > 20: Route Groups
• Mehrstufige Navigation: Nested Layouts
• Mehrere unabhängige Module gleichzeitig: Parallel Routes
• Modal-Interaktion: Intercepting Routes + Parallel Routes - 2
Step 2: Route Groups für Funktionsbereiche anlegen
Mit Klammern Route Groups erstellen:
• (marketing): Marketing-Seiten (Start, Über uns, Preise)
• (shop): Shop (Produkte, Warenkorb)
• (dashboard): Admin (Bestellungen, Benutzer)
Hinweise:
• Group-Namen beeinflussen die URL nicht; dieselbe URL darf nicht in mehreren Groups vorkommen
• Jede Route Group kann eigenes layout.js haben
• page.js der Startseite muss in einer Route Group liegen (nicht app/page.js bei mehreren Root-Layouts) - 3
Step 3: Nested-Layout-Hierarchie entwerfen
Nach UI-Hierarchie planen:
• Ebene 1: app/layout.js (site-weit: Header + Footer)
• Ebene 2: Bereichs-layout.js (z. B. blog/layout.js mit Sidebar)
• Ebene 3: Detail-layout.js (z. B. blog/[slug]/layout.js mit Inhaltsverzeichnis)
Umsetzung:
• Jede Ebene nur eigene UI-Elemente
• Kind-Layouts erben vom Eltern-Layout
• layout wird beim Seitenwechsel nicht neu gerendert – gute Performance - 4
Step 4: Parallel Routes implementieren (falls nötig)
Slots mit @ anlegen:
• @modal: Modal-Slot
• @sales, @orders: unabhängige Dashboard-Module
In layout.js empfangen:
• export default function Layout({ children, modal, sales, orders })
• Im JSX: {modal} {sales} {orders}
Jeder Slot kann eigenes loading.js und error.js haben - 5
Step 5: Intercepting Routes implementieren (für Modals)
Intercepting-Struktur anlegen:
• Unter @modal: (.)photos/[id]/page.js (gleichstufige Route abfangen)
• Parallel: photos/[id]/page.js (Vollseite)
Syntax:
• (.): gleichstufig
• (..): eine Ebene höher
• (...): von Root
default.js mit return null ist Pflicht, damit Modals schließen - 6
Step 6: Testen und validieren
Funktionen prüfen:
• Route Groups: URL unverändert, Struktur klar
• Nested Layouts: UI-Hierarchie und State-Erhalt
• Parallel Routes: unabhängiges Laden, Fehlerisolierung
• Intercepting Routes: Modal bei Client-Navigation, Vollseite bei Refresh
Performance:
• Next.js DevTools für Layout-Render-Zähler
• layout soll beim Unterrouten-Wechsel nicht neu rendern
FAQ
Beeinflussen Route Groups die URL?
Wann sollte ich Route Groups verwenden?
Beeinträchtigen Nested Layouts die Performance?
Was ist der Unterschied zwischen Parallel Routes und normalen Komponenten?
Wie wähle ich (.), (..) oder (...) bei Intercepting Routes?
Warum braucht Intercepting Routes default.js?
Muss ich beim Wechsel von Pages Router zu App Router die Routen umbauen?
8 Min. Lesezeit · Veröffentlicht am: 18. Dez. 2025 · Aktualisiert am: 14. Juli 2026
Next.js Komplettleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
Next.js Pages Router zu App Router: Praxisleitfaden mit Migrationsstrategie und Fallen-Checkliste
Pages Router zu App Router migrieren? Dieser Artikel liefert eine schrittweise Migrationsstrategie, Lösungen für 7 häufige Fallen und Erfahrungen aus echten Produktionsprojekten – für ein sicheres Upgrade auf Next.js 14.
Teil 3 von 51
Nächster
Next.js App Router in der Praxis: Route Groups und Nested Layouts gegen Verzeichnis-Chaos in großen Projekten
Mit Route Groups, Nested Layouts, Parallel Routes und Intercepting Routes lösen Sie Verzeichnis-Chaos, Routing-Konflikte und Team-Kollaborationsprobleme in großen Next.js-Projekten – inklusive direkt übernehmbarer Verzeichnisstruktur.
Teil 5 von 51
Ähnliche Beiträge
Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick

Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick
Next.js 15 in der Praxis: Wie ich an einem Wochenende ein produktionsreifes Blog-System aufgebaut habe


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen