Next.js App Router Advanced mit Claude Code 2026

Der Next.js App Router hat Features die kaum jemand kennt — Parallel Routes für Modals, Intercepting Routes für Instagram-ähnliche UX, Middleware für Auth und alle vier Caching-Layers. Claude Code kennt alle Advanced Features für Next.js 15.

Next.js 15 ist nicht nur ein React-Framework — es ist ein vollständiges Full-Stack-Ökosystem mit ausgefeilten Routing-, Caching- und Rendering-Konzepten. Wer nur die Basics kennt (page.tsx, layout.tsx, API-Routes), verschenkt enormes Potenzial. Dieser Guide zeigt die sechs Advanced Features, die Claude Code im Jahr 2026 souverän beherrscht und für dich implementiert.

Claude Code versteht die Ordnerstruktur des App Routers auf einer tiefen Ebene — es kennt die Konventionen, die Besonderheiten und die Pitfalls. Du beschreibst das Feature, Claude Code baut es richtig. Kein Stack Overflow, kein Trial-and-Error mit obskuren Fehlermeldungen.

1. Parallel Routes — @slot Konvention und Modal-Pattern

PARALLEL Was sind Parallel Routes?

Parallel Routes ermöglichen es, mehrere Seiten gleichzeitig innerhalb desselben Layouts zu rendern. Die Slots werden über die @slotname-Ordnerkonvention definiert und als Props in das übergeordnete Layout injiziert. Jeder Slot hat seinen eigenen Navigationszustand, eigene Loading- und Error-States.

Das klassische Anwendungsbeispiel ist ein Dashboard mit mehreren unabhängigen Bereichen — Analytics-Graph auf der linken Seite, aktuelle Bestellungen rechts, beide laden separat und können unabhängig voneinander Fehler werfen. Claude Code baut dieses Pattern auf Anfrage vollständig auf.

app/
├── dashboard/
│   ├── @analytics/          # Slot 1 — wird als Prop "analytics" übergeben
│   │   ├── page.tsx
│   │   ├── loading.tsx
│   │   └── error.tsx
│   ├── @orders/             # Slot 2 — wird als Prop "orders" übergeben
│   │   ├── page.tsx
│   │   └── loading.tsx
│   ├── @modal/              # Slot 3: Modal-Slot
│   │   ├── default.tsx      # PFLICHT: null wenn kein Modal aktiv
│   │   └── (.)photo/
│   │       └── [id]/
│   │           └── page.tsx
│   ├── layout.tsx           # Empfängt alle Slots als Props
│   └── page.tsx
// app/dashboard/layout.tsx — Parallel Routes Layout
import { ReactNode } from 'react'

interface DashboardLayoutProps {
  children: ReactNode
  analytics: ReactNode    // @analytics Slot
  orders: ReactNode       // @orders Slot
  modal: ReactNode        // @modal Slot
}

export default function DashboardLayout({
  children,
  analytics,
  orders,
  modal,
}: DashboardLayoutProps) {
  return (
    <div className="dashboard-wrapper">
      <main className="dashboard-main">{children}</main>
      <aside className="analytics-panel">{analytics}</aside>
      <section className="orders-section">{orders}</section>
      {modal}
    </div>
  )
}
Claude Code Prompt-Tipp: "Erstelle ein Dashboard mit drei Parallel-Route-Slots: @analytics für Umsatzgraphen, @notifications für Benachrichtigungen und @modal für ein Photo-Modal. Nutze die Next.js App Router @slot-Konvention mit default.tsx."

Das Modal-Pattern mit Parallel Routes

Das mächtigste Parallel-Routes-Pattern ist das Modal-Pattern: Ein Foto-Klick auf einer Feed-Seite öffnet das Bild in einem Modal, während der Feed-Hintergrund erhalten bleibt. Bei direktem Aufruf der URL wird das Foto als eigene Seite angezeigt. Instagram und Pinterest nutzen genau dieses UX-Muster.

// app/dashboard/@modal/default.tsx — Pflicht-Datei!
// Wenn kein Modal-Slot aktiv ist, MUSS default.tsx null zurückgeben
export default function ModalDefault() {
  return null
}

// app/dashboard/@modal/(.)photo/[id]/page.tsx
// (.) = gleiche Route-Ebene intercepten
import { Modal } from '@/components/Modal'
import { getPhoto } from '@/lib/photos'

export default async function PhotoModal({
  params,
}: {
  params: { id: string }
}) {
  const photo = await getPhoto(params.id)

  return (
    <Modal>
      <img src={photo.url} alt={photo.alt} className="modal-image" />
      <p className="modal-caption">{photo.caption}</p>
    </Modal>
  )
}
Achtung: default.tsx ist bei Parallel Routes mit Intercepting Routes zwingend erforderlich. Fehlt diese Datei, wirft Next.js einen 404-Fehler wenn der Slot keine aktive Route hat. Claude Code fügt diese Datei automatisch hinzu.

2. Intercepting Routes — (.)route und (..)route Konventionen

INTERCEPT Soft Navigation vs. Hard Navigation

Intercepting Routes fangen eine Navigation ab und zeigen stattdessen einen anderen Inhalt — aber nur bei Soft Navigation (Client-side). Bei direktem URL-Aufruf oder Reload wird die echte Zielseite gerendert. Das ermöglicht die klassische "Modal bei Klick, Seite bei direktem Aufruf"-UX.

Die Konvention basiert auf der relativen Tiefe der abzufangenden Route. Claude Code kennt alle vier Varianten und wählt die richtige automatisch basierend auf der gewünschten Ordnerstruktur.

PrefixBedeutungBeispiel
(.)Gleiche Route-Ebene(.)photo/[id] fängt photo/[id] auf gleicher Ebene ab
(..)Eine Ebene höher(..)photo/[id] fängt Route eine Ebene über dem aktuellen Segment ab
(...)Root-Ebene(...)photo/[id] fängt Route vom App-Root ab
(..)(..)Zwei Ebenen höherZwei Ebenen aufsteigen vor dem Intercepten
app/
├── feed/
│   ├── page.tsx                   # Feed-Seite mit Foto-Grid
│   ├── @modal/
│   │   ├── default.tsx            # null — kein Modal aktiv
│   │   └── (.)photo/              # (.) = gleiche Ebene wie /feed/photo/
│   │       └── [id]/
│   │           └── page.tsx       # Modal-Ansicht bei Soft Nav
│   └── layout.tsx                 # Empfängt @modal Slot
└── photo/
    └── [id]/
        └── page.tsx               # Vollseite bei Hard Nav / direktem URL
// app/feed/layout.tsx — Parallel + Intercepting Routes kombiniert
export default function FeedLayout({
  children,
  modal,
}: {
  children: React.ReactNode
  modal: React.ReactNode
}) {
  return (
    <>
      {children}
      {modal}
    </>
  )
}

// app/feed/page.tsx — Feed mit klickbaren Fotos
import Link from 'next/link'
import { getPhotos } from '@/lib/photos'

export default async function FeedPage() {
  const photos = await getPhotos()

  return (
    <div className="photo-grid">
      {photos.map((photo) => (
        <Link key={photo.id} href={`/photo/${photo.id}`}>
          <img src={photo.thumbnail} alt={photo.alt} />
        </Link>
      ))}
    </div>
  )
  // Bei Klick: Modal (Soft Nav) | Bei direktem URL: /photo/[id] Seite
}

// app/feed/@modal/(.)photo/[id]/page.tsx — Wird bei Soft Nav angezeigt
import { PhotoModal } from '@/components/PhotoModal'

export default async function InterceptedPhotoPage({
  params,
}: {
  params: { id: string }
}) {
  return <PhotoModal photoId={params.id} />
}
Praxis-Tipp: Das Modal braucht eine Schließen-Funktion via router.back() — nicht via router.push(). Bei Push würde der User in der History feststecken. Claude Code implementiert das korrekt mit dem useRouter Hook im Client Component.

3. Middleware — Auth-Redirect, matcher config und Geo-Routing

MIDDLEWARE middleware.ts — Wo und wie

Die middleware.ts Datei liegt im Root des Projekts (neben package.json), nicht im app/ Verzeichnis. Sie läuft auf dem Edge Runtime vor jedem Request und ermöglicht Request-Transformation, Redirects, Rewrites und Header-Manipulation ohne die Seite zu laden.

mein-projekt/
├── app/
│   └── ...
├── middleware.ts            # Im Root — NICHT in app/
├── next.config.ts
└── package.json

Auth-Redirect mit NextAuth / Clerk

// middleware.ts — Auth-basierte Redirects
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'

export async function middleware(request: NextRequest) {
  const token = await getToken({
    req: request,
    secret: process.env.NEXTAUTH_SECRET,
  })

  const { pathname } = request.nextUrl

  // Geschützte Routen: /dashboard und alle Subrouten
  if (pathname.startsWith('/dashboard') && !token) {
    const loginUrl = new URL('/login', request.url)
    loginUrl.searchParams.set('callbackUrl', pathname)
    return NextResponse.redirect(loginUrl)
  }

  // Admin-Bereich: zusätzliche Rollenprüfung
  if (pathname.startsWith('/admin')) {
    if (!token || token.role !== 'admin') {
      return NextResponse.redirect(new URL('/403', request.url))
    }
  }

  // Login-Seite: eingeloggte User zum Dashboard leiten
  if (pathname === '/login' && token) {
    return NextResponse.redirect(new URL('/dashboard', request.url))
  }

  return NextResponse.next()
}

// matcher: Welche Routen werden von Middleware verarbeitet?
export const config = {
  matcher: [
    // Alle Pfade außer statische Assets und Next.js internals
    '/((?!_next/static|_next/image|favicon.ico|public).*)',
    // Explizite Pfade: cleaner und verständlicher
    '/dashboard/:path*',
    '/admin/:path*',
    '/login',
  ],
}

Geo-basiertes Routing — Länder-spezifische Inhalte

// middleware.ts — Geo-Routing via Vercel Geo Headers
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const GEO_BLOCKED_COUNTRIES = ['RU', 'BY']
const LOCALE_REDIRECTS: Record<string, string> = {
  'DE': '/de',
  'AT': '/de',
  'CH': '/de',
  'US': '/en',
  'GB': '/en',
}

export function middleware(request: NextRequest) {
  // Vercel injiziert Geo-Daten als Header
  const country = request.headers.get('x-vercel-ip-country') ?? 'US'
  const { pathname } = request.nextUrl

  // Geo-Block: gesperrte Länder → 403
  if (GEO_BLOCKED_COUNTRIES.includes(country)) {
    return NextResponse.redirect(new URL('/geo-blocked', request.url))
  }

  // Sprach-Redirect: Root → lokalisierte Version
  if (pathname === '/' && LOCALE_REDIRECTS[country]) {
    return NextResponse.redirect(
      new URL(LOCALE_REDIRECTS[country], request.url)
    )
  }

  // Custom Header hinzufügen (verfügbar in Server Components)
  const response = NextResponse.next()
  response.headers.set('x-user-country', country)
  return response
}

export const config = {
  matcher: [
    '/',
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}
matcher Syntax erklärt: Der matcher akzeptiert einfache Pfade ('/dashboard/:path*') und Regex-Pattern. Das Pattern (?!_next/static) ist ein Negative Lookahead — es schließt alle Pfade aus, die mit _next/static beginnen. Claude Code generiert den richtigen matcher für jeden Use Case.

4. Edge Runtime — runtime: 'edge', Limitierungen und Use Cases

EDGE Was ist die Edge Runtime?

Die Edge Runtime ist eine eingeschränkte JavaScript-Laufzeitumgebung die auf Cloudflare Workers / Vercel Edge Network basiert. Sie startet in Millisekunden (kein Cold Start), ist global verteilt und extrem schnell — aber hat strenge Einschränkungen: Keine Node.js APIs, keine nativen Module, kein Dateisystem-Zugriff.

Edge Runtime aktivieren

// In einer Route Handler Datei (app/api/fast/route.ts)
export const runtime = 'edge'  // Edge Runtime für diese Route

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const name = searchParams.get('name') ?? 'World'

  return new Response(
    JSON.stringify({ message: `Hello, ${name}!`, runtime: 'edge' }),
    { headers: { 'Content-Type': 'application/json' } }
  )
}

// In einem Server Component (app/fast-page/page.tsx)
export const runtime = 'edge'  // Gesamte Seite auf Edge rendern

export default function FastPage() {
  return <h1>Diese Seite läuft auf der Edge Runtime</h1>
}

Was ist erlaubt — was nicht?

FeatureEdge RuntimeNode.js Runtime
Web APIs (fetch, Request, Response)
Web Crypto API
TextEncoder / TextDecoder
ReadableStream / WritableStream
Node.js fs (Dateisystem)
Node.js path, os, crypto
Native Module (*.node)
Prisma ORM (standard)
Prisma Accelerate
Drizzle ORM (mit HTTP-Driver)
Globale Latenz (Vercel)~5-15ms~50-200ms

Ideale Use Cases für Edge Runtime

// Use Case 1: A/B-Testing auf Edge-Ebene
export const runtime = 'edge'

export async function GET(request: Request) {
  // Zufällige Variante zuweisen (kein DB-Call nötig)
  const variant = Math.random() > 0.5 ? 'A' : 'B'

  const response = NextResponse.next()
  response.cookies.set('ab-variant', variant, {
    maxAge: 60 * 60 * 24 * 30, // 30 Tage
    httpOnly: true,
  })
  return response
}

// Use Case 2: JWT-Validierung ohne DB-Round-trip
import { jwtVerify } from 'jose'  // Edge-kompatibel!

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  try {
    await jwtVerify(
      token,
      new TextEncoder().encode(process.env.JWT_SECRET)
    )
    return NextResponse.next()
  } catch {
    return NextResponse.redirect(new URL('/login', request.url))
  }
}
export const runtime = 'edge'
Wichtig: Prisma mit Standard-Driver funktioniert NICHT auf Edge Runtime — es nutzt intern Node.js APIs. Verwende stattdessen Prisma Accelerate (HTTP-basiert) oder Drizzle ORM mit dem Neon/PlanetScale HTTP-Driver. Claude Code kennt die Edge-kompatiblen Alternativen.

5. Metadata API — generateMetadata, opengraph-image.tsx, sitemap.ts und robots.ts

META Statisch vs. Dynamisch

Next.js 15 bietet zwei Wege für Metadata: Statisches Export-Objekt für feste Werte und generateMetadata() für dynamische Werte die auf params oder Datenbankdaten basieren. Beide werden auf dem Server ausgeführt und korrekt in den HTML-Head gerendert.

generateMetadata() — Dynamische Meta-Tags

// app/blog/[slug]/page.tsx — Dynamische Metadata
import type { Metadata, ResolvingMetadata } from 'next'
import { getPost } from '@/lib/posts'

type Props = {
  params: { slug: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const post = await getPost(params.slug)

  // Parent-Metadata erben (z.B. base OpenGraph-Image)
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: post.title,
    description: post.excerpt,
    authors: [{ name: post.author.name }],
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      images: [
        {
          url: post.ogImage || `/api/og?title=${encodeURIComponent(post.title)}`,
          width: 1200,
          height: 630,
          alt: post.title,
        },
        ...previousImages,
      ],
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.ogImage],
    },
    alternates: {
      canonical: `https://example.com/blog/${params.slug}`,
    },
  }
}

opengraph-image.tsx — Dynamische OG-Images

// app/blog/[slug]/opengraph-image.tsx
// Next.js rendert dies als PNG automatisch
import { ImageResponse } from 'next/og'
import { getPost } from '@/lib/posts'

export const runtime = 'edge'
export const alt = 'Blog Post OG Image'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

export default async function OGImage({
  params,
}: {
  params: { slug: string }
}) {
  const post = await getPost(params.slug)

  return new ImageResponse(
    (
      <div
        style={{
          background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
          width: '100%',
          height: '100%',
          display: 'flex',
          flexDirection: 'column',
          padding: '60px',
          justifyContent: 'flex-end',
        }}
      >
        <div style={{ color: 'white', fontSize: 60, fontWeight: 700 }}>
          {post.title}
        </div>
        <div style={{ color: 'rgba(255,255,255,0.8)', fontSize: 28, marginTop: 16 }}>
          {post.author.name} — {new Date(post.publishedAt).toLocaleDateString('de-DE')}
        </div>
      </div>
    ),
    { ...size }
  )
}

sitemap.ts und robots.ts

// app/sitemap.ts — Dynamische Sitemap
import type { MetadataRoute } from 'next'
import { getAllPosts, getAllProducts } from '@/lib/data'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts()
  const products = await getAllProducts()

  const blogUrls = posts.map((post) => ({
    url: `https://example.com/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: 'weekly' as const,
    priority: 0.8,
  }))

  const productUrls = products.map((product) => ({
    url: `https://example.com/products/${product.slug}`,
    lastModified: new Date(product.updatedAt),
    changeFrequency: 'daily' as const,
    priority: 0.9,
  }))

  return [
    { url: 'https://example.com', changeFrequency: 'yearly', priority: 1 },
    { url: 'https://example.com/blog', changeFrequency: 'weekly', priority: 0.9 },
    ...blogUrls,
    ...productUrls,
  ]
}

// app/robots.ts — robots.txt generieren
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/admin/', '/api/', '/private/'],
      },
      {
        userAgent: 'Googlebot',
        allow: '/',
      },
    ],
    sitemap: 'https://example.com/sitemap.xml',
  }
}

6. Next.js Caching Layers — alle vier Ebenen erklärt

CACHE Die vier Caching-Ebenen von Next.js 15

Next.js hat ein mehrschichtiges Caching-System das automatisch greift. Das Verstehen aller vier Ebenen ist entscheidend um Bugs zu vermeiden ("warum sehe ich alte Daten?") und Performance zu optimieren. Claude Code kennt alle vier und kann gezielt einzelne Ebenen invalidieren.

1. Request Memoization

Duplizierte fetch()-Calls mit gleicher URL + Options werden innerhalb eines Renders dedupliziert. Automatisch, kein Config nötig.

2. Data Cache

Persistent über Requests und Deployments. fetch() cached standardmäßig. Invalidierbar via revalidateTag() oder revalidatePath().

3. Full Route Cache

Gesamte gerenderte HTML+RSC-Payload wird auf dem Server gespeichert. Nur für statisch gerenderte Seiten. Wird bei Deployment neu gebaut.

4. Router Cache

Client-seitiger Cache im Browser. RSC-Payloads werden nach Navigation gespeichert. Läuft nach Zeit ab (30s für statisch, 0s für dynamisch).

Request Memoization — Automatische Deduplication

// Request Memoization: gleiche URL = einmal gefetcht
// Auch wenn beide Komponenten in einer Render-Pass aufgerufen werden

// UserAvatar.tsx
async function UserAvatar({ userId }: { userId: string }) {
  const user = await fetch(`/api/users/${userId}`).then(r => r.json())
  return <img src={user.avatar} alt={user.name} />
}

// UserGreeting.tsx
async function UserGreeting({ userId }: { userId: string }) {
  // GLEICHER fetch-Call → wird NICHT zweimal ausgeführt
  const user = await fetch(`/api/users/${userId}`).then(r => r.json())
  return <p>Hallo, {user.name}!</p>
}
// → Nur 1 HTTP-Request, beide Komponenten bekommen gleiche Daten

Data Cache — Konfiguration und Invalidierung

// Data Cache Optionen bei fetch()
const data = await fetch('https://api.example.com/posts', {
  next: {
    revalidate: 3600,         // ISR: nach 3600s neu fetchen (time-based)
    tags: ['posts', 'blog'],  // Tags für gezielte Invalidierung
  }
})

// Kein Cache: immer frische Daten (Dynamic Rendering)
const liveData = await fetch('https://api.example.com/live', {
  cache: 'no-store'
})

// Server Action: Cache-Invalidierung on-demand
// app/actions.ts
'use server'

import { revalidateTag, revalidatePath } from 'next/cache'

export async function publishPost(postId: string) {
  // Post in DB speichern...
  await savePostToDb(postId)

  // Data Cache für 'posts' Tag invalidieren
  revalidateTag('posts')

  // Oder spezifischen Pfad invalidieren
  revalidatePath('/blog')
  revalidatePath(`/blog/${postId}`)
}

// Route Handler: On-Demand Revalidation via Webhook
// app/api/revalidate/route.ts
export async function POST(request: Request) {
  const { searchParams } = new URL(request.url)
  const tag = searchParams.get('tag')
  const secret = searchParams.get('secret')

  if (secret !== process.env.REVALIDATION_SECRET) {
    return new Response('Unauthorized', { status: 401 })
  }

  if (tag) {
    revalidateTag(tag)
    return new Response(`Revalidated tag: ${tag}`)
  }

  return new Response('No tag provided', { status: 400 })
}

Full Route Cache vs. Router Cache

// Full Route Cache: Seite als static markieren
// app/blog/page.tsx
export const revalidate = 3600  // ISR: alle Stunde neu generieren
// oder:
export const revalidate = 0     // Immer dynamisch (kein Full Route Cache)
// oder:
export const dynamic = 'force-static'   // Immer static
export const dynamic = 'force-dynamic'  // Immer dynamic

// generateStaticParams: Welche Seiten beim Build generiert werden
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}
// → Alle bekannten Blog-Posts werden beim Build statisch generiert
// → Neue Posts: on-demand SSR + dann gecacht (ISR-Verhalten)

// Router Cache: Client-seitiger Cache Reset
'use client'

import { useRouter } from 'next/navigation'

function RefreshButton() {
  const router = useRouter()

  return (
    <button onClick={() => router.refresh()}>
      Seite aktualisieren
    </button>
    // router.refresh() = Router Cache leeren + Server neu abfragen
  )
}
Cache-Debugging: Im Development-Modus (next dev) ist der Data Cache standardmäßig deaktiviert — jeder Request fetcht frische Daten. Im Production-Build greift der Cache. Claude Code hilft bei der Analyse von Cache-Bugs mit gezielten Diagnose-Fragen.

Caching-Entscheidungsbaum

// Wann welche Caching-Strategie?

// STATIC: Inhalt ändert sich selten (Blog, Docs, Marketing)
export const revalidate = 86400  // 24h
export const dynamic = 'force-static'

// ISR: Inhalt ändert sich gelegentlich (E-Commerce, News)
export const revalidate = 60  // 1 Minute
// + revalidateTag() bei Mutations

// DYNAMIC: Inhalt ist user-spezifisch (Dashboard, Profil)
export const dynamic = 'force-dynamic'
// + fetch mit cache: 'no-store'

// HYBRID: Mischung (Layout statisch, Content dynamisch)
// Layout: statisch + langer revalidate
// Komponentenebene: Suspense + Streaming für dynamische Teile

// unstable_noStore: Granulare Cache-Kontrolle auf Komponentenebene
import { unstable_noStore as noStore } from 'next/cache'

async function LiveCounter() {
  noStore()  // Diese Komponente nie cachen
  const count = await getLiveCount()
  return <span>{count} online</span>
}

Fazit: Claude Code als Next.js Advanced Experte

Die sechs Advanced Features — Parallel Routes, Intercepting Routes, Middleware, Edge Runtime, Metadata API und Caching — sind das, was Next.js von einfachen React-Setups unterscheidet. Sie sind leistungsstark, aber haben Konventionen und Pitfalls die schwer zu googeln sind.

Claude Code kennt alle diese Features auf einer tiefen, praktischen Ebene. Es wählt die richtige Ordnerstruktur (@slot, (.)-Prefix, default.tsx), schreibt korrekte TypeScript-Typen und erklärt warum es welche Entscheidung trifft. Das spart Stunden an Debugging und Stack-Overflow-Suchen.

Im Claude Code Mastery Kurs bauen wir ein vollständiges Next.js 15 Projekt das alle diese Features in einem realen Kontext einsetzt — von der Photosharing-App mit Parallel Routes über ein Auth-System mit Middleware bis hin zu einer optimierten E-Commerce-Seite mit ISR und On-Demand Revalidation.

Next.js-Modul im Kurs

Im Claude Code Mastery Kurs: Next.js 15 App Router von Grundlagen bis Advanced — Parallel Routes, Middleware, Edge Runtime, Metadata API und alle vier Caching-Layers.

14 Tage kostenlos testen →