Next.js Performance-Optimierung 2026: TTFB, Core Web Vitals und der Profiling-Workflow, der echte Engpässe findet
Next.js Performance-Optimierung sieht 2026 anders aus, als es die meisten Guides beschreiben. Next.js 15 bringt Partial Prerendering, React 19, die neuen asynchronen Request-APIs und deutlich verbesserte Instrumentation-Unterstützung mit - was bedeutet, dass Techniken, die in Next.js 13 oder 14 funktioniert haben, heute sogar kontraproduktiv sein können. Wichtiger noch: Die meisten Teams überspringen den Messschritt komplett. Sie greifen reflexartig zu Dynamic Imports oder Server Components, ohne vorher zu prüfen, ob das, was sie ändern, überhaupt der Engpass ist.
Dieser Guide behebt genau das. Starte mit dem Profiling-Workflow, der echte Regressionen sichtbar macht, und arbeite dich dann durch die vier Optimierungsbereiche mit dem größten Hebel: TTFB, JavaScript-Bundle-Größe, Bild- und Font-Auslieferung sowie Datenbank-Query-Performance.
Der Profiling-Workflow vor allem anderen
Der häufigste Performance-Fehler ist Optimierung nach Bauchgefühl. Der zweithäufigste ist, Lighthouse einmal auf localhost laufen zu lassen und das Thema abzuhaken. Beides liefert keine verwertbaren Daten darüber, was in Produktion tatsächlich langsam ist.
Ein messungsbasierter Workflow für Next.js 15 besteht aus vier Komponenten.
Next.js Instrumentation Hooks. Die Datei instrumentation.ts im Projekt-Root stellt onRequestError und den register-Hook bereit. Noch nützlicher: Du kannst Data-Fetching-Aufrufe innerhalb von Server Components mit performance.now()-Timern umschließen und die Ergebnisse in ein strukturiertes Log schreiben. In Produktion fließen diese Logs direkt in deinen Observability-Stack - Datadog, Grafana oder auch einfach stdout nach CloudWatch. Der Punkt ist, Server-Renderzeiten pro Request zu bekommen, nicht nur aggregierte Seiten-Scores.
@next/bundle-analyzer. Installieren, das Webpack-Plugin einbinden und ANALYZE=true next build ausführen. Die Treemap zeigt dir exakt, welche Pakete in welchen Client-Bundles landen. Achte auf die Spalte First Load JS im Build-Output - Next.js gibt sie nach jedem Build aus. Jede Route über 250 kB ist einen Blick wert. Jede Route über 500 kB ist ein Problem.
Vercel Speed Insights oder dein Äquivalent. Labordaten aus Lighthouse decken sich nicht mit Felddaten. Real User Monitoring zeigt dir, was echte Besucher auf echten Geräten erleben. Wenn du nicht auf Vercel bist, liefert dir die Web-Vitals-Library dasselbe Signal mit ein paar Zeilen Code in deiner app/layout.tsx.
Lighthouse CI in deiner GitHub-Actions-Pipeline. Ein Performance-Score, der sich über 50 Pull Requests hinweg still verschlechtert, ist ein schlimmeres Ergebnis als ein fehlgeschlagener CI-Check. Konfiguriere budgets in deiner lighthouserc.js mit Schwellenwerten, die zu deiner Zielgruppe passen - ein B2B-Produkt, das tagsüber am Desktop genutzt wird, verträgt weniger aggressive Mobile-Budgets als eine Consumer-App. Ein sinnvoller Startpunkt für ein B2B-SaaS: LCP ≤ 2,5 s, CLS ≤ 0,1, INP ≤ 200 ms, TTFB ≤ 800 ms. Lass den Build fehlschlagen, wenn ein Schwellenwert auf den Hauptrouten gerissen wird.
Erst wenn diese vier Bausteine stehen, ergibt es Sinn, zu den Optimierungstechniken unten zu greifen.
TTFB reduzieren mit Partial Prerendering
TTFB ist die Time to First Byte: die Zeitspanne zwischen dem Absenden eines Requests durch den Browser und dem Empfang des ersten Bytes der Antwort. Bei serverseitig gerenderten Seiten setzt sie sich zusammen aus Infrastruktur-Latenz, Cold-Start-Zeit (relevant bei Lambda-basierten Deployments) und Server-Renderzeit. In Next.js 15 verändert PPR die Denkweise über den dritten Faktor.
Partial Prerendering erlaubt dir, eine statische Shell - die Teile einer Route, die nicht von dynamischen Daten abhängen - mit nahezu null TTFB von der CDN-Edge auszuliefern, während die dynamischen Abschnitte suspended werden. Die Schlüsselentscheidung ist, wo die PPR-Grenze verläuft. Komponenten, die cookies(), headers() oder eine nutzerabhängige Datenbank-Query lesen, gehören in eine <Suspense>-Boundary. Navigation, Hero-Bereiche, statische Marketing-Texte und Seitenstruktur gehören nach außen.
Auch die Route-Segment-Konfiguration zählt. export const revalidate = 3600 auf einer Route zu setzen, die keine frischen Daten pro Request braucht, ist oft die TTFB-Verbesserung mit dem geringsten Aufwand. Die Seite wird einmal generiert und eine Stunde lang aus dem Cache ausgeliefert. Wenn selbst eine Stunde zu alt ist, nimm revalidate = 60 mit Stale-while-revalidate-Semantik - Nutzer bekommen sofort eine gecachte Antwort, während Next.js den Cache im Hintergrund auffrischt.
Für Routen, die sich wirklich nicht cachen lassen, optimiere die Server-Renderzeit direkt: Blockiere nicht das gesamte Rendering mit einer einzelnen langsamen Datenbank-Query, verschiebe langsame Queries in parallele Promise.all()-Aufrufe und lagere unkritische Data-Fetches in streamende Suspense-Boundaries aus.
JavaScript-Bundle-Optimierung
Viel clientseitiges JavaScript ist die häufigste Ursache für langsames LCP und schlechtes INP. Die Lösung ist meist nicht, die Komponente neu zu schreiben - sondern sicherzustellen, dass Client-JavaScript nur dann ausgeliefert wird, wenn der Browser es wirklich braucht.
Der größte Gewinn in den meisten Next.js-Codebases liegt beim Barrel-File-Problem. Ein Barrel File (eine index.ts, die alles aus einem Verzeichnis re-exportiert) sieht harmlos aus. Aber wenn du eine einzelne Utility aus einem Barrel importierst, das auch eine Chart-Library, einen Date Picker und eine Animations-Library re-exportiert, landet all das in deinem Bundle, selbst wenn du es nie benutzt. Die Regel eslint-plugin-barrel-files markiert diese Muster beim Linten. Die Lösung sind direkte Imports: import { formatDate } from '@/lib/dates/formatDate' statt import { formatDate } from '@/lib'.
Dynamic Imports mit next/dynamic solltest du Komponenten vorbehalten, die wirklich groß sind und beim ersten Render nicht gebraucht werden: Rich-Text-Editoren, Datenvisualisierungs-Libraries, Karten-Komponenten, PDF-Viewer. Greife nicht zu Dynamic Imports als allgemeine Performance-Technik - der Lazy-Loading-Overhead erhöht die Latenz bei Interaktionen und kann INP verschlechtern, wenn die Komponente schnell gebraucht wird.
Die React-Server-Component-Migration ist die strukturelle Lösung. Wenn eine Komponente kein useState, useEffect, keine Event-Handler und keine Browser-APIs nutzt, sollte sie eine Server Component sein. Server Components liefern null JavaScript an den Client aus. Bei einem kürzlichen Projekt-Audit hat das Verschieben des authentifizierten Dashboard-Layouts, der Datentabellen und der Sidebar-Navigation von Client zu Server Components das initiale JS-Bundle dieser Route um 60% reduziert. Die Anwendungslogik hat sich nicht geändert - nur der Ort, an dem sie läuft.
Bild- und Font-Pipeline
next/image erledigt den Großteil der harten Arbeit, aber Konfigurationslücken sind häufig. Zwei Einstellungen, die du prüfen solltest: remotePatterns und Blur-Placeholder.
Wenn du Bilder von einem externen CDN oder Object-Storage-Bucket auslieferst, konfiguriere remotePatterns in next.config.ts mit exaktem Hostname und Protokoll. Das schaltet die Optimierung für externe Bilder frei und macht das veraltete domains-Array überflüssig. Generiere für jedes Content-Bild mit festem Seitenverhältnis zur Build-Zeit eine Blur-Data-URL und übergib sie als blurDataURL mit placeholder="blur". Das kostet ein paar Millisekunden im Build und eliminiert den Layout-Shift beim Laden der Bilder - was CLS direkt verbessert.
Google Fonts ist für die meisten Projekte die Standardwahl, führt aber einen Cross-Origin-Request ein, der das Font-Rendering blockiert. Next.js bringt Self-Hosting für Google Fonts mit: Wenn du eine Schrift aus next/font/google importierst, lädt Next.js die Font-Dateien zur Build-Zeit herunter und liefert sie ohne zusätzliche Konfiguration von deiner eigenen Domain aus. Wenn du noch einen <link>-Tag in app/layout.tsx auf fonts.googleapis.com zeigen hast, wechsle zu next/font/google und der Cross-Origin-Request verschwindet sofort.
Datenbank-Query-Performance in Server Components
Server Components machen Data Fetching einfacher als das alte getServerSideProps-Modell, führen aber einen neuen Fehlermodus ein: die N+1-Query.
Das Muster ist leicht zu übersehen: Du lädst eine Liste von zehn Projekten, und innerhalb der Rendering-Schleife macht jede Projektkarte ihren eigenen Datenbankaufruf, um die Details des Projektinhabers zu laden. Zehn Projekte bedeuten elf Queries. Hundert Projekte bedeuten hunderteins. Auf Seiten, die innerhalb eines Layouts rendern, das ebenfalls Daten lädt, verstärkt sich das Problem über verschachtelte Komponenten, die jeweils unabhängige Aufrufe machen.
Die Lösung ist Batching. Mit Prisma nutzt du include oder explizites select, um verknüpfte Daten in einer einzigen Query zu laden. Mit Drizzle nutzt du Joins. Wenn du mit einer API statt einer Datenbank arbeitest, parallelisiert Promise.all() unabhängige Fetches, ohne sie zu eliminieren - nicht so effizient wie Batching, aber deutlich besser als sequenzielle Awaits in einer Schleife.
Connection Pooling ist auf Serverless-Deployments kritisch. Auf Lambda oder Vercel Functions öffnet potenziell jede Funktionsausführung eine neue Datenbankverbindung. Ohne Pooler erschöpft eine Traffic-Spitze das Verbindungslimit der Datenbank in Minuten. Konfiguriere PgBouncer oder nutze das eingebaute Pooling von Supabase mit pgbouncer=true im Connection String. Setze Verbindungslimits in deiner Prisma- oder Drizzle-Konfiguration konservativ - eine Pool-Größe von 1 pro Lambda-Funktion ist oft die richtige Antwort, da jede Funktion einen Request zur Zeit bearbeitet.
Performance-Budget-Vorlage
Als praktischer Abschluss hier die Lighthouse-CI-Schwellenwerte, die Wolf-Tech in produktiven Next.js-Projekten für B2B-SaaS-Anwendungen einsetzt. Sie werden bei jedem Pull Request auf den kritischen Pfaden durchgesetzt: der Login-Seite, dem authentifizierten Haupt-Dashboard und der konversionsstärksten Marketing-Route.
| Metrik | Warnung | Fehler |
|---|---|---|
| LCP | > 2,0 s | > 2,5 s |
| CLS | > 0,05 | > 0,1 |
| INP | > 150 ms | > 200 ms |
| TTFB | > 600 ms | > 800 ms |
| First Load JS (pro Route) | > 200 kB | > 300 kB |
Alles außerhalb dieser Grenzen lässt CI fehlschlagen und erfordert eine bewusste Merge-Entscheidung. Diese Reibung ist gewollt - sie erzwingt ein Gespräch darüber, ob die Regression akzeptabel ist, bevor sie ausgeliefert wird, statt Monate später, wenn ein Dutzend anderer Änderungen die Isolierung erschwert haben.
Performance-Arbeit ohne Mess-Loop ist Wartungsschuld mit Extraschritten. Wenn du es mit einer Next.js-Anwendung zu tun hast, deren Performance sich über mehrere Release-Zyklen verschlechtert hat, und nicht weißt, wo du anfangen sollst, ist ein strukturiertes Next.js Performance-Audit meist der schnellste Weg, die Änderungen mit dem größten Hebel zu finden.
Du erreichst das Wolf-Tech-Team unter hello@wolf-tech.io oder über wolf-tech.io, um zu besprechen, wie ein Profiling-Engagement für deine konkrete Anwendung aussieht.

