next/image repariert Ihren LCP nicht
Die Komponente optimiert Bytes. Über den LCP entscheidet, wann die Anfrage startet - meist die Auffindreihenfolge, ein lazy geladenes Hero oder eine blockierende Schrift.
Teams kommen zu uns, nachdem sie bereits jedes <img> durch next/image
ersetzt haben, und der LCP hat sich nicht bewegt. Das ist das erwartbare
Ergebnis, und es lohnt sich zu verstehen, warum - denn dieselbe Überlegung
zeigt, was stattdessen geholfen hätte.
next/image optimiert die Nutzlast: ein kleineres Format, die passenden
Maße für das Gerät, kein Layout-Shift. Der LCP misst den Zeitpunkt des
größten Paints. Bytes sind eine Eingangsgröße dafür, und meistens nicht die
bindende.
Was darüber entscheidet, wann die Anfrage startet
Der Browser kann nur holen, was er gefunden hat. Für das Hero-Bild gibt es drei Möglichkeiten, und sie liegen eine Größenordnung auseinander:
| Wie das Bild gefunden wird | Wann die Anfrage startet |
|---|---|
<img> im initialen HTML, eager | Beim Preload-Scan, vor dem CSS |
<img> im initialen HTML, lazy | Nach dem Layout, sobald der Browser weiß, dass es sichtbar ist |
| Von JavaScript nach der Hydration eingefügt | Nachdem das Bundle geladen, geparst und ausgeführt wurde |
next/image lädt standardmäßig lazy. Die übliche Migration macht also aus
einem eager geladenen Hero ein lazy geladenes, verzögert seine Anfrage bis
nach das Layout und verschlechtert den LCP - während jedes Bild-Audit in
Lighthouse grün wird.
Die Korrektur ist ein Prop:
import Image from 'next/image';
export function Hero() {
return (
<Image
src="/hero.jpg"
alt="..."
width={1600}
height={900}
priority // eager + fetchpriority="high" + ein Preload-Hinweis
sizes="100vw"
className="w-full h-auto"
/>
);
}priority auf genau einem Bild pro Route - auf dem, das das LCP-Element ist.
Es auf sechs Bilder zu setzen ist dasselbe, wie es auf keines zu setzen, weil
alle um dieselbe Verbindung konkurrieren.
Der sizes-Fehler, der mehr kostet als das Format
sizes sagt dem Browser, welchen Kandidaten aus srcset er nehmen soll, und
wird vor dem Layout ausgewertet. Steht dort das Falsche, laden Sie ein
1600px-Bild für einen 400px-Platz - oder, schlimmer für die Qualität, ein
400px-Bild für ein vollflächiges Banner.
Der Standard beim Weglassen ist 100vw, was für ein vollbreites Hero richtig
und für alles andere falsch ist:
// Ein Kartenbild in einem dreispaltigen Raster.
<Image
src={post.cover}
alt=""
width={800}
height={450}
sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"
/>Dieses eine Attribut ist meist mehr wert als die AVIF-Konvertierung, weil es ändert, welche Datei angefragt wird, statt wie viele Bytes diese Datei hat.
Wenn das LCP-Element Text ist
Auf einer Content-Website ist es das meistens, und dann geht die Bildarbeit am Thema vorbei. Die Überschrift kann nicht painten, bevor ihre Schrift aufgelöst ist; die Kette lautet also HTML → CSS → Schriftdatei → Paint.
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // im Fallback painten, tauschen wenn die Datei da ist
variable: '--font-sans',
});next/font hostet die Datei selbst und gibt ein Preload aus, was die
Drittanbieter-Verbindung aus der Kette nimmt. display: 'swap' ist das, was
die Überschrift überhaupt nicht mehr warten lässt - der Preis ist ein
sichtbarer Tausch, den Sie über einen Fallback mit ähnlichen Metriken
verkleinern und nicht dadurch, dass Sie den Paint blockieren.
Die andere Hälfte ist der Fallback-Stack. font-family: var(--font-sans), sans-serif
lässt den Browser sofort in einer Schrift painten, deren Metriken Sie nicht
gewählt haben; eine nahe Systemschrift zu benennen macht den Tausch deutlich
unauffälliger.
Das Richtige messen
Lighthouse meldet Labor-LCP bei einem kalten, gedrosselten Ladevorgang. Felddaten melden, was Ihre Besucher erlebt haben, inklusive Cache-Treffern und echter Geräte. Die beiden widersprechen sich ständig, und die Feldzahl ist die maßgebliche.
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function Vitals() {
useReportWebVitals((metric) => {
if (metric.name !== 'LCP') return;
// Das Element, nicht nur die Zahl - "LCP ist 3,1 s" ist nicht
// handlungsfähig, "LCP ist 3,1 s und es ist das Hero-Bild" schon.
navigator.sendBeacon('/api/vitals', JSON.stringify({
value: metric.value,
element: metric.attribution?.element,
url: location.pathname,
}));
});
return null;
}Loggen Sie das Element. Die Hälfte unserer Performance-Projekte beginnt damit,
dass das LCP-Element nicht das ist, was das Team annahm - ein Cookie-Banner,
ein verborgenes <h1>, ein leerer Container, der zufällig groß ist.
Die vollständige Reihenfolge, was zuerst zu beheben ist, steht in Core Web Vitals in Next.js: was die Zahlen wirklich bewegt.
Die Kurzfassung
- Finden Sie heraus, welches Element der LCP ist - aus Felddaten, pro Route.
- Ist es ein Bild:
priorityauf genau dieses eine, korrektessizes, und sorgen Sie dafür, dass nichts darüber im HTML den Parser blockiert. - Ist es Text:
next/fontmitdisplay: 'swap'und ein Fallback mit nahen Metriken. - Erst danach über Format und Qualität nachdenken.
In Schritt eins bis drei liegen die Sekunden. In Schritt vier liegen die Audits - weshalb er derjenige ist, der zuerst gemacht wird.
