Next.js 16: Der komplette Migrations-Guide

Next.js 16 wurde am 21. Oktober 2025 veröffentlicht und bringt massive Änderungen: Turbopack ist jetzt Standard, ein komplett neues Caching-System mit "use cache", und zahlreiche Breaking Changes rund um async APIs. Dieser Guide hilft bei der Migration.

Die wichtigsten Neuerungen

1. Turbopack ist jetzt Standard (Stable)

Turbopack ersetzt Webpack als Default-Bundler für alle Next.js-Apps:

Performance-Verbesserungen:

  • 2-5x schnellere Production Builds
  • 10x schnellere Fast Refresh in Dev
  • 📦 Über 50% aller Dev-Sessions nutzen bereits Turbopack (Next.js 15.3+)

Wichtig: Du kannst Webpack weiterhin nutzen:

# Dev mit Webpack
next dev --webpack
 
# Build mit Webpack
next build --webpack

Turbopack File System Caching (Beta):

// next.config.ts
const nextConfig = {
  experimental: {
    turbopackFileSystemCacheForDev: true, // Schnellere Restarts
  },
};
 
export default nextConfig;

2. Cache Components – Das neue Caching-System

Das größte Feature in Next.js 16: Opt-in Caching mit "use cache":

Wichtig: Alle Pages sind jetzt standardmäßig dynamisch (nicht mehr statisch!):

// next.config.ts
const nextConfig = {
  cacheComponents: true, // Cache Components aktivieren
};
 
export default nextConfig;

Komponenten cachen:

// app/components/product-list.tsx
import { cacheLife } from "next/cache";
 
export async function ProductList() {
  "use cache"; // Diese Komponente wird gecacht
  cacheLife("hours"); // 1 Stunde Cache
 
  const products = await fetch("https://api.example.com/products").then((r) => r.json());
 
  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>
          {p.name} - {p.price}
        </li>
      ))}
    </ul>
  );
}

Ganze Pages cachen:

// app/blog/page.tsx
import { cacheLife } from "next/cache";
 
export default async function BlogPage() {
  "use cache";
  cacheLife("days"); // 1 Tag Cache
 
  const posts = await fetch("https://cms.example.com/posts").then((r) => r.json());
 
  return <div>{/* ... */}</div>;
}

Cache-Profile:

import { cacheLife } from "next/cache";
 
// Built-in Profiles
cacheLife("minutes"); // 5 Minuten (Standard)
cacheLife("hours"); // 1 Stunde
cacheLife("days"); // 1 Tag
cacheLife("max"); // 1 Jahr
 
// Custom Profile
cacheLife({
  revalidate: 3600, // 1 Stunde
  stale: 7200, // 2 Stunden stale erlaubt
});

3. Neue Caching APIs

a) revalidateTag() – Jetzt mit SWR (Stale-While-Revalidate):

import { revalidateTag } from "next/cache";
 
// ✅ NEU: Zweiter Parameter erforderlich
revalidateTag("blog-posts", "max"); // SWR mit 1 Jahr Cache
 
// ⚠️ DEPRECATED: Funktioniert nicht mehr
revalidateTag("blog-posts");

b) updateTag() – NEU: Read-Your-Writes:

"use server";
 
import { updateTag } from "next/cache";
 
export async function updateUserProfile(userId: string, profile: Profile) {
  await db.users.update(userId, profile);
 
  // Benutzer sieht Änderungen SOFORT (nicht stale)
  updateTag(`user-${userId}`);
}

c) refresh() – NEU: Nur dynamische Daten refreshen:

"use server";
 
import { refresh } from "next/cache";
 
export async function markNotificationAsRead(notificationId: string) {
  await db.notifications.markAsRead(notificationId);
 
  // Nur ungecachte Daten refreshen (z.B. Notification-Count)
  refresh();
}

4. React 19.2 Features

Next.js 16 nutzt React 19.2 mit neuen APIs:

View Transitions:

import { useTransition, ViewTransition } from "react";
 
function ProfileCard({ user }) {
  const [isPending, startTransition] = useTransition();
 
  return (
    <ViewTransition>
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
    </ViewTransition>
  );
}

useEffectEvent:

import { useEffect, useEffectEvent } from "react";
 
function ChatRoom({ roomId }) {
  const onMessage = useEffectEvent((msg) => {
    // Diese Funktion wird nicht neu erstellt bei jedem Re-Render
    showNotification(msg);
  });
 
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.on("message", onMessage);
    return () => connection.disconnect();
  }, [roomId]); // onMessage NICHT in Dependencies!
}

5. proxy.ts (vorher middleware.ts)

Breaking Change: middleware.tsproxy.ts:

// proxy.ts (NEU)
export default function proxy(request: NextRequest) {
  return NextResponse.redirect(new URL("/Startseite", request.url));
}

Migration:

# Datei umbenennen
mv middleware.ts proxy.ts
 
# Funktion umbenennen
# middleware() → proxy()

Wichtig: middleware.ts ist deprecated und wird in Zukunft entfernt!

Breaking Changes

1. Async params, searchParams, cookies, headers

Alle diese APIs sind jetzt async:

// ❌ ALT (Next.js 15)
export default function Page({ params, searchParams }) {
  const id = params.id;
  const query = searchParams.q;
  return <div>{id}</div>;
}
 
// ✅ NEU (Next.js 16)
export default async function Page({
  params,
  searchParams,
}: {
  params: Promise<{ id: string }>;
  searchParams: Promise<{ q?: string }>;
}) {
  const { id } = await params;
  const { q } = await searchParams;
  return <div>{id}</div>;
}

cookies() und headers():

// ❌ ALT
import { cookies } from "next/headers";
 
export default function Page() {
  const token = cookies().get("auth-token");
  return <div>{token}</div>;
}
 
// ✅ NEU
import { cookies } from "next/headers";
 
export default async function Page() {
  const cookieStore = await cookies();
  const token = cookieStore.get("auth-token");
  return <div>{token}</div>;
}

2. fetch() ist jetzt standardmäßig NICHT gecacht

Riesiger Breaking Change:

// ❌ ALT: fetch() war automatisch gecacht
const data = await fetch("https://api.example.com/data");
 
// ✅ NEU: Explizites Caching erforderlich
const data = await fetch("https://api.example.com/data", {
  next: { revalidate: 3600 }, // 1 Stunde Cache
});
 
// Oder: "use cache" Directive
async function getData() {
  "use cache";
  return fetch("https://api.example.com/data");
}

3. next/image Änderungen

a) Lokale Bilder mit Query Strings benötigen localPatterns:

// next.config.ts
const nextConfig = {
  images: {
    localPatterns: [
      {
        pathname: "/assets/images/**",
        search: "", // Erlaubt Query Strings
      },
    ],
  },
};

b) Neue Defaults:

// Neue Defaults in Next.js 16
const nextConfig = {
  images: {
    minimumCacheTTL: 14400, // 4 Stunden (vorher 60s)
    imageSizes: [32, 48, 64, 96, 128, 256, 384], // 16 entfernt
    qualities: [75], // Vorher [1..100]
    maximumRedirects: 3, // Vorher unlimited
    dangerouslyAllowLocalIP: false, // NEU: Sicherheits-Flag
  },
};

4. Entfernte Features

Vollständig entfernt:

  • AMP Support (useAmp, export const config = { amp: true })
  • next lint (nutze eslint direkt)
  • serverRuntimeConfig / publicRuntimeConfig (nutze .env)
  • experimental.ppr (jetzt cacheComponents)
  • experimental.dynamicIO (jetzt cacheComponents)
  • unstable_rootParams() (Alternative kommt bald)

Codemod nutzen:

npx @next/codemod@canary upgrade latest

Migration Schritt-für-Schritt

Schritt 1: Dependencies aktualisieren

# Next.js 16 + React 19.2
npm install next@latest react@latest react-dom@latest
 
# TypeScript-Typen aktualisieren
npm install -D @types/react@latest @types/react-dom@latest
 
# ESLint-Config aktualisieren
npm install -D eslint-config-next@latest

Schritt 2: Breaking Changes automatisch fixen

# Offizieller Codemod
npx @next/codemod@canary upgrade latest
 
# Oder manuell einzelne Codemods:
npx @next/codemod@canary next-async-request-api .
npx @next/codemod@canary metadata-to-viewport-export .

Schritt 3: middleware.ts → proxy.ts

# Datei umbenennen
git mv middleware.ts proxy.ts
// proxy.ts
// Funktion umbenennen: middleware → proxy
export default function proxy(request: NextRequest) {
  // Deine Logik bleibt gleich
  return NextResponse.next();
}

Schritt 4: Async params/searchParams fixen

Quick Fix mit TypeScript:

// Globale Type Helper
type AwaitedParams<T> = T extends Promise<infer U> ? U : T;
 
// In deinen Pages
type PageParams = { id: string };
 
export default async function Page({ params }: { params: Promise<PageParams> }) {
  const { id } = await params;
  // ...
}

Schritt 5: fetch() Caching überarbeiten

Strategie 1: next.revalidate nutzen:

// Überall wo fetch() war:
const data = await fetch(url, {
  next: { revalidate: 3600 }, // 1 Stunde
});

Strategie 2: "use cache" nutzen (empfohlen):

// config aktivieren
// next.config.ts: cacheComponents: true
 
async function getData() {
  "use cache";
  cacheLife("hours");
  return fetch(url);
}

Schritt 6: Cache Components aktivieren

// next.config.ts
const nextConfig = {
  cacheComponents: true,
};
 
export default nextConfig;

Dann Pages opt-in cachen:

// app/products/page.tsx
import { cacheLife } from "next/cache";
 
export default async function ProductsPage() {
  "use cache";
  cacheLife("max"); // 1 Jahr mit SWR
 
  // Page wird gecacht, bleibt aber aktuell durch Background-Revalidation
  return <div>...</div>;
}

Häufige Probleme & Lösungen

Problem 1: Build schlägt fehl mit "params is not defined"

Ursache: params nicht awaited

Lösung:

// ❌ Falsch
export default function Page({ params }) {
  console.log(params.id); // Error!
 
// ✅ Richtig
export default async function Page({ params }) {
  const { id } = await params;
  console.log(id);
}

Problem 2: fetch() liefert alte Daten trotz "use cache"

Ursache: cacheComponents nicht aktiviert

Lösung:

// next.config.ts
const nextConfig = {
  cacheComponents: true, // MUSS aktiviert sein!
};

Problem 3: Turbopack Build bricht ab

Ursache: Custom Webpack Loaders inkompatibel

Lösung:

# Fallback auf Webpack
next build --webpack
 
# Oder: Turbopack-Adapter konfigurieren
// next.config.ts
const nextConfig = {
  turbopack: {
    rules: {
      "*.svg": ["@svgr/webpack"], // Custom Loader
    },
  },
};

Problem 4: TypeScript Errors mit async params

Lösung: Types explizit definieren:

// types/params.ts
export type PageProps<T = {}> = {
  params: Promise<T>;
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};
 
// In Page nutzen
import type { PageProps } from "@/types/params";
 
export default async function Page({ params, searchParams }: PageProps<{ id: string }>) {
  const { id } = await params;
  const { q } = await searchParams;
  // ...
}

Performance-Best Practices

1. Cache Components strategisch nutzen

// ✅ CACHEN: Relativ statische Inhalte
export async function BlogPostList() {
  "use cache";
  cacheLife("hours");
  // ...
}
 
// ❌ NICHT CACHEN: User-spezifische Daten
export async function UserDashboard() {
  // Kein "use cache" - immer fresh
  const user = await getCurrentUser();
  return <div>Welcome {user.name}</div>;
}

2. Partial Prerendering (PPR) mit Cache Components

// app/dashboard/page.tsx
import { Suspense } from "react";
 
export default async function Dashboard() {
  return (
    <>
      {/* Statisch (gecacht) */}
      <Header />
      <Navigation />
 
      {/* Dynamisch (on-demand) */}
      <Suspense fallback={<Skeleton />}>
        <UserProfile />
      </Suspense>
 
      <Suspense fallback={<Skeleton />}>
        <RealtimeData />
      </Suspense>
    </>
  );
}

3. Bundle Analyzer nutzen

# Bundle-Größe analysieren
npm install -D @next/bundle-analyzer
// next.config.ts
import bundleAnalyzer from "@next/bundle-analyzer";
 
const withBundleAnalyzer = bundleAnalyzer({
  enabled: process.env.ANALYZE === "true",
});
 
export default withBundleAnalyzer({
  cacheComponents: true,
});
# Analyse starten
ANALYZE=true npm run build

4. React Compiler aktivieren (optional)

# React Compiler installieren
npm install babel-plugin-react-compiler@latest
// next.config.ts
const nextConfig = {
  reactCompiler: true, // Automatische Memoisierung
};

Achtung: Build-Zeit erhöht sich, da React Compiler Babel nutzt!

Node.js & Browser Requirements

Neue Mindestanforderungen:

| Requirement | Version | | ----------- | ------------- | | Node.js | 20.9.0+ (LTS) | | TypeScript | 5.1.0+ | | Chrome | 111+ | | Edge | 111+ | | Firefox | 111+ | | Safari | 16.4+ |

Node.js 18 wird nicht mehr unterstützt!

Entfernte Deprecations

Diese Features wurden komplett entfernt:

// ❌ Funktioniert nicht mehr
export const config = { amp: true };
import { useAmp } from "next/amp";
 
// ✅ Alternativen
// Nutze reguläre Next.js Pages ohne AMP

PowerShell 2.0 Skripte migrieren:

# Scripts auf PS 5+ prüfen
Get-ChildItem -Recurse -Filter "*.ps1" |
    Select-String "-Version 2"
 
# PowerShell 7+ installieren
winget install Microsoft.PowerShell

Fazit: Lohnt sich das Upgrade?

JA, wenn:

  • ✅ Du von Turbopack profitieren willst (5-10x Fast Refresh!)
  • ✅ Du mehr Kontrolle über Caching brauchst ("use cache")
  • ✅ React 19.2 Features nutzen möchtest (View Transitions, useEffectEvent)
  • ✅ Dein Projekt aktiv entwickelt wird

WARTEN, wenn:

  • ⏸️ Du viele custom Webpack Loaders hast (erst Kompatibilität prüfen)
  • ⏸️ Dein Code stark auf implizites fetch()-Caching baut
  • ⏸️ Du AMP nutzt (wird nicht mehr unterstützt!)

Empfohlene Timeline:

  1. Woche 1-2: Test-Branch mit next@latest + Codemod
  2. Woche 3: Breaking Changes manuell fixen, Tests grün machen
  3. Woche 4: cacheComponents testen, Performance messen
  4. Woche 5+: Schrittweiser Rollout mit Monitoring

Wichtig: Nutze npx @next/codemod@canary upgrade latest für automatische Migration!


Quelle: Next.js 16 – Release-Ankündigung

Hinweis: Alle Inhalte wurden mit größter Sorgfalt erstellt, dennoch übernehme ich keine Gewähr für Aktualität, Vollständigkeit oder technische Auswirkungen. Eine Haftung für Schäden, die aus der Umsetzung entstehen, ist ausgeschlossen.


Probleme bei der Migration? Kontaktieren Sie POETSCHKE-IT für professionelle Next.js 16 Migration und Performance-Optimierung!