Zum Inhalt springen
Alle Artikel

Lighthouse 100 am Desktop mit Next.js 16: die Hebel, die den Score bewegt haben

16. Sept. 2026 · Web-Entwicklung · 4 min

·Von Dimitri Pisarev

Diese Seite (Next.js 16, App Router, jede Seite statisch vorgerendert) erreicht Lighthouse 100 am Desktop gleichzeitig für Performance, Accessibility, Best Practices und SEO. Die Hebel dorthin sind konkret und wenige: das gesamte CSS inline im HTML, Above-the-fold-Critical-CSS mit beiden Themes, ein servergerenderter Header ohne Navigations-Hydration und lazy-hydratierte Islands mit festen Platzhaltern. Mobil liegt die Seite ehrlich bei 77 bis 84, und das ist React-Hydration, keine kaputte Konfiguration.

Welche Hebel haben den Score wirklich bewegt?#

HebelWas er eliminiert
experimental.inlineCssDie render-blockierende Stylesheet-Anfrage
Critical CSS mit beiden ThemesFirst-Paint-Layout-Shift und Theme-Flash
Server-only-HeaderNavigation aus den Hydrations-Kosten
Lazy Demo-Island, fester PlatzhalterBelow-fold-JavaScript und CLS
font-display: optional + Idle-ArbeitFont-Swap-Geflimmer, Main-Thread-Konkurrenz
Shiki zur Build-ZeitEinen Laufzeit-Highlighter

Das gesamte CSS im Dokument. Mit experimental.inlineCss legt Next das komplette Stylesheet in jede HTML-Seite statt es zu verlinken. Für eine statische Seite, deren Stylesheet klein genug für die ersten Antworten ist, ist eine zusätzliche Round-Trip-Anfrage ein reiner Verlust: Das inline CSS gewinnt vor jeder Optimierung des CSS selbst. Das ist der größte Desktop-Hebel und kostet ein Config-Flag.

Critical CSS, das beide Themes trägt. Das Theme hier ist ein data-theme-Attribut über CSS-Custom-Properties, dunkel als Voreinstellung mit einer hellen Variante. Die subtile Falle: Der inline Critical-Block muss beide Themes definieren. Liefert man nur die dunklen Variablen aus, ist der erste Paint nach dem Umschalten auf hell eine kaputte Kaskade. Die Art Bug, die als Screenshot von einem Nutzer kommt, nicht im eigenen Testlauf. Ein Block, beide Variablensätze, inline im Head.

Ein Header, der nichts hydratiert. Der Header ist eine Server-Komponente: Logo, Navigation und Locale-Wechsel sind schlichtes HTML. Die interaktiven Teile (Theme-Umschalter, das Mobile-Menü als natives details-Element, der Command-Palette-Trigger) sind kleine Client-Islands, die unabhängig hydratisieren, und der Palette-Knopf arbeitet über einen delegierten Listener statt eines Handlers im Markup. Die Navigation, die jede einzelne Seite rendert, kostet null JavaScript.

Below-fold-Islands mit festem Platzhalter. Die interaktive Telegram-Shop-Demo auf der Landing-Page hydratisiert über einen IntersectionObserver mit rootMargin: 0: Nichts lädt, bis sie wirklich gescrollt wird. Ihr Platzhalter reserviert die exakte Endgröße (379 mal 509 Pixel), damit CLS bei null bleibt, selbst auf der langsamsten Verbindung. Lazy ohne Größenreservierung tauscht Geflimmer nur gegen Layout-Shift.

Fonts und Idle-Arbeit. font-display: optional entfernt Swap-Geflimmer: Ist die Schrift nicht rechtzeitig da, rendert der Fallback, und niemand sieht Text springen. Nicht-kritische Arbeit wartet auf requestIdleCallback. Syntax-Highlighting passiert zur Build-Zeit mit Shiki. Kein Highlighter fährt im Browser mit.

Warum bleibt Mobil unter 100?#

Weil alles oben Netzwerk- und Main-Thread-Kosten zurückkauft, die React-Hydration danach wieder ausgibt. Unter 4-fachem CPU-Throttling landet die Total Blocking Time bei rund 500 bis 650 Millisekunden: Die Seiten kommen vollständig an, dann hydratisiert React i18n, Theming, Palette und Lese-Fortschritt auf dem Main Thread. Lighthouse sieht das und bewertet Mobil mit 77 bis 84, alles außer Performance bereits bei 100.

Die ehrlichen Optionen an diesem Punkt: ein Static Export mit Islands-only-Interaktivität, oder ein Framework, dessen Rendering-Modell am Server beginnt (der Vergleich in Livewire vs. React-SPA ist dieselbe Entscheidung in Anwendungsgröße). Beides sind Architekturänderungen, keine Konfiguration. Ich habe sie gegen den Zweck der Seite abgewogen und die React-Version behalten: Mobil 77 bis 84 mit sofortiger Navigation auf echten Geräten schlägt eine abgeschlossene 100.

Gotcha

Lighthouse auf dem Produktions-Build laufen lassen, nicht auf next dev. Der Dev-Server injiziert Overhead, der zehn Punkte kosten kann und einen Dinge optimieren lässt, die bereits in Ordnung sind. Jede Zahl in diesem Artikel stammt aus next build plus next start.

Was der Score nicht zeigt#

Eine 100 ist kein Zertifikat. Die Hebel oben lohnen sich für ihre sichtbaren Effekte allein: Das Stylesheet blockiert das Rendern nicht, das Theme flackert nicht, und die Navigation funktioniert, bevor JavaScript überhaupt geparsed ist. Die Zahl ist ein Nebeneffekt dieser Entscheidungen. Umgekehrt gejagt entsteht genau die Art Seite, die 100 score-t und sich langsam anfühlt. Behandle deshalb jeden Hebel zuerst als Nutzererfahrung und messe danach neu.