Forced Reflow in Lighthouse: was das bedeutet und der Fix
Ein Skript, das die Seite ändert und danach eine Größe abfragt, zwingt den Browser, das Layout sofort neu zu berechnen. Wann das Taps hängen lässt und wann es harmlos ist.
Was bedeutet Forced Reflow in Lighthouse?
Ein Forced Reflow bedeutet: Ein Skript hat deine Seite geändert und danach eine Größe abgefragt. Der Browser musste das Layout deshalb sofort neu berechnen. Lighthouse listet solche Skripte im Insight „Forced reflow“ auf („A forced reflow occurs when JavaScript queries geometric properties“). Einmal ist normal; in einer Schleife hängen Taps. Lass CSS die Arbeit des Skripts erledigen, oder lies jede Größe aus, bevor du etwas änderst.
Table of Contents!
Was listet der Hinweis „Forced reflow“ auf?
Lighthouse 13 und PageSpeed Insights beschreiben es so: „A forced reflow occurs when JavaScript queries geometric properties (such as offsetWidth) after styles have been invalidated by a change to the DOM state. This can result in poor performance. Learn more about forced reflows and possible mitigations.“ Der Hinweis wird rot, wenn Skripte beim Laden der Seite 30 ms oder mehr an Layout-Arbeit auf einen Schlag erzwingen.
Der folgende Bericht stammt von einer Übersichtsseite für Ferienhütten, die ich wie eine WordPress-Website für Ferienhäuser aufgebaut habe. Das Theme-Skript kürzt jede Beschreibung auf drei Zeilen und macht alle Karten gleich hoch.

Lighthouse 13.5 auf Mobile, DevTools-Throttling (Slow 4G, 4x CPU).
- Top function call: die äußere Funktion, die die Layout-Arbeit gestartet hat. Fahre mit der Maus über den gekürzten Dateipfad, um den vollständigen Pfad zu sehen – er nennt das Theme, das Plugin oder die App. Eine Datei auf einer anderen Domain ist meist ein Drittanbieter-Tag, etwa ein Session-Recorder (Skripte, die du nicht ändern kannst).
- Source: jede Codezeile, die eine Größe abgefragt hat.
- Total reflow time: die Layout-Zeit, die diese Funktion oder Zeile verursacht hat.
- Unscored: Das Insight hat kein Gewicht in deinem Performance-Score. Die Long Tasks dahinter (Phasen über 50 ms, in denen die Seite nicht reagieren kann) senken den Score über die Total Blocking Time.
Die oberste Zeile (388 ms) ist niedriger als die Source-Zeilen zusammen (743 ms), weil das Hütten-Skript beim Laden zweimal läuft – und die oberste Zeile nur den größeren Durchlauf anzeigt.
Warum zwingt ein Skript den Browser, das Layout neu zu berechnen?
Auf der Hütten-Übersicht von oben friert ein Tap auf den Filter „All“ die Seite für eine Drittelsekunde ein, bevor die 48 Karten erscheinen.
Beim Layout ermittelt der Browser, wo jedes Element sitzt und wie groß es ist. Normalerweise passiert das einmal pro Frame, nachdem die Skripte durchgelaufen sind. Wenn ein Skript die Seite ändert (einen Text, eine Klasse, eine Höhe) und danach eine Größe ausliest – etwa offsetHeight, getBoundingClientRect(), scrollTop oder getComputedStyle() –, muss der Browser das Layout sofort mitten im Skript berechnen, um die Antwort zu liefern. Paul Irish pflegt die vollständige Liste aller Abfragen, die einen Reflow erzwingen.
Ein einzelner Forced Reflow ist meist nur Layout-Arbeit, die der Browser einen Moment später ohnehin erledigt hätte. In einer Schleife, die ändert und wieder liest, wird daraus in jeder Runde ein komplettes Layout: Layout Thrashing. Das Theme der Hütten-Seite kürzt Beschreibungen so:
while (el.offsetHeight > max && words.length > 1) {
words.pop();
el.textContent = words.join(' ') + '…';
} Jedes entfernte Wort ändert den Text. Liest das Skript danach wieder offsetHeight aus, erzwingt das ein neues Layout: einmal pro Wort, auf jeder einzelnen Karte.
Wie viel langsamer macht ein Forced Reflow eine Seite?
Ich habe die Hütten-Übersicht mit dem Theme-Skript gemessen (Vorher) und mit CSS, das den Text abschneidet und die Karten gleich hoch macht (Nachher). Beide Versionen sehen identisch aus.
| Vorher | Nachher | |
|---|---|---|
| Lighthouse-Score | 84 | 92 |
| Total Blocking Time | 360 ms | 0 ms |
| Total reflow time | 743 ms | keine |
| Ein Tap auf „All“ | 320 ms | 16 ms |

Vorher

Nachher
Lighthouse 13.5 auf Mobile, DevTools-Throttling.
Die 8 Punkte Unterschied kommen von der Total Blocking Time: Die erzwungenen Layouts blockieren die Seite länger. Den Tap spürt dein Besucher direkt – und er zählt für den INP (Interaction to Next Paint), den Core Web Vital für die Reaktionszeit auf Taps und Klicks. 320 ms liegen über dem Grenzwert von 200 ms für einen guten INP, und 298 ms davon waren Layout-Arbeit, die das Skript erzwungen hat; 16 ms entsprechen einem einzelnen Frame. Lighthouse führt selbst keine Taps aus. Ich habe die Taps daher in Chrome gemessen, mit auf Smartphone-Niveau gedrosselter CPU.
Wann ist ein Forced Reflow harmlos?
- Eine kurze Zeile beim Laden. Eine Zeile von knapp über 30 ms ist oft nur das erste Layout der Seite – etwas vorgezogen, weil ein Skript eine Größe abfragt. Das Theme-Skript der Hütten-Seite – umgeschrieben, sodass es zuerst alle Höhen liest – zeigte in einem von vier Lighthouse-Durchläufen eine Zeile von 37 ms, und der Tap auf „All“ dauerte 16 ms.
- Eine Zeile „[unattributed]“. Sie nennt keine Codezeile: Lass Lighthouse noch einmal laufen. Wenn die Zeile weg ist, ignoriere sie.
- Ein Skript, das eine Animation startet. Modals in Bootstrap und manche Popup-Plugins lesen absichtlich eine Größe aus, damit ihre CSS-Transition anspringt.

Lighthouse 13.5 auf Mobile, DevTools-Throttling.
Behebe das Problem, wenn die gesamte Reflow-Zeit mehrere hundert Millisekunden beträgt, wenn eine Datei viele Zeilen erzeugt oder wenn das Skript auch bei Taps läuft: etwa bei einem Filter, einem Menü oder einem Slider.
Welches Skript verursacht den Forced Reflow bei einem Tap?
Lighthouse lädt die Seite nur. Zeichne Taps, die hängen, deshalb im Performance-Panel auf:
- Öffne die DevTools (F12), wechsle zu Performance und stelle die CPU auf 4x Slowdown, damit sich dein Laptop wie ein Smartphone verhält.
- Klicke auf Record, tippe an, was sich langsam anfühlt, und stoppe die Aufzeichnung.
- Öffne Forced reflow in der Insights-Seitenleiste. „Call frames that trigger reflow“ nennt die Funktion und die Zeile.

Ein Tap auf „All“ auf der Hütten-Übersicht: clampText in cabins.js.
Die Konsole meldet dasselbe als „[Violation] Forced reflow while executing JavaScript took 38ms“, sobald du in den Log-Levels die Stufe Verbose aktivierst.
Nennt Lighthouse nur jquery.min.js, hat jQuery die Abfrage für ein Theme oder Plugin ausgeführt. Zeichne im Performance-Panel ein Neuladen der Seite auf und klicke auf einen Layout-Block mit rotem Dreieck: Im Summary unter Function stack ist die Datei zwischen den Zeilen von jQuery – mit einem Pfad wie /wp-content/plugins/<name>/ – das Plugin.
Deine field data enthält die Taps und Klicks deiner echten Besucher. Öffne in CoreDash den Bereich INP, gehe auf Breakdown und gruppiere nach LOAF: Chrome zeichnet auf, welche Skripte in jedem langsamen Frame (Long Animation Frames) liefen. Jede Zeile steht für ein Skript samt dem INP der Interaktionen, an denen es beteiligt war. Ein Forced Reflow in einem Klick- oder Tap-Handler zählt als Processing, der lila Teil jedes Balkens. Starte also mit einem langsamen Skript, dessen lila Anteil lang ist.

INP pro Skript in CoreDash, Desktop, p75: Grün ist Input Delay, Lila Processing, Blau Presentation.
Wie behebst du einen Forced Reflow?
Starte mit der Datei in Top function call und prüfe, ob CSS die Aufgabe des Skripts übernehmen kann. Auf drei Zeilen gekürzter Text (line-clamp), gleich hohe Karten (Grid), ein fixierter Header (position: sticky) oder eine Bildbox, die ihre Form behält (aspect-ratio), brauchen kein Skript – und ohne Skript erzwingt nichts ein Layout.
Zeichne nach jeder Änderung denselben Tap erneut auf: Forced reflow sollte aus der Insights-Seitenleiste verschwunden sein. Prüfe das Ergebnis danach in Lighthouse und in deinen field data; die Search Console braucht bis zu 28 Tage.
WordPress-Themes, Plugins und Page-Builder
Der Pfad in Top function call nennt das Theme (/wp-content/themes/) oder das Plugin (/wp-content/plugins/). Suche zuerst nach einer Einstellung. Elementors „Stretch Section“ verbreitert einen Abschnitt bei jedem Laden und jedem Resize per JavaScript; das Seitenlayout „Elementor Full Width“ mit einem Full-Width-Container erledigt das komplett in CSS. Ein Swiper-Slider mit „auto“ bei den Slides per View vermisst jeden Slide; mit einer festen Zahl berechnet er die Breiten, ohne sie auszulesen.
Verzögerst du JavaScript mit WP Rocket oder einem ähnlichen Plugin, verschwindet die Meldung zwar – das Skript läuft dann aber beim ersten Tap oder Scroll des Besuchers mit denselben erzwungenen Layouts. Behebe stattdessen das Skript oder melde es dem Entwickler des Plugins und tausche das Plugin aus, wenn sich nichts ändert.
Kürzt das Theme Text per JavaScript, schneide ihn stattdessen per CSS in deinem Child-Theme ab – mit der Textklasse deines Themes:
.cabin .desc {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
overflow: hidden;
} Schalte danach das Clamp-Skript des Themes ab: über eine Theme-Option oder per wp_dequeue_script() mit dem entsprechenden Handle.
Shopify-Themes und Apps
Schalte im Theme-Editor unter App embeds die Apps einzeln nacheinander aus und starte Lighthouse jeweils neu. Verschwindet der Hinweis, melde die App dem Entwickler oder ersetze sie. Ein Pfad mit /assets/ in Top function call ist eine Theme-Datei – aus dem Theme selbst oder von einer App, die dort Code ergänzt hat. Dawn und darauf aufbauende Themes lesen eine Größe meist nur einmal aus und bündeln ihre Änderungen. Sieh dir deshalb zuerst die Apps an.
Eigenes JavaScript und React
Muss ein Skript messen, lies zuerst alle Größen aus und ändere danach das DOM – so reicht ein einziges Layout für alle Abfragen. Ein Beispiel mit einem Akkordeon, das jedes Panel auf seine eigene Höhe setzt:
const panels = [...document.querySelectorAll('.panel')];
// Ein Layout für jedes Panel: lesen, schreiben, lesen, schreiben
panels.forEach(p => { p.style.maxHeight = p.scrollHeight + 'px'; });
// Ein einziges Layout: alle Höhen zuerst lesen, dann alle schreiben
const heights = panels.map(p => p.scrollHeight);
panels.forEach((p, i) => { p.style.maxHeight = heights[i] + 'px'; }); - Resize und Scroll: ResizeObserver und IntersectionObserver liefern die Größen aus dem Layout, das der Browser gerade berechnet hat. Nutze sie, statt Größen in einem Resize- oder Scroll-Handler auszulesen.
- React: Miss einmal in
useLayoutEffectund setze den State einmal. Mounte Menüs, Popover und Tooltips erst beim Öffnen – geschlossene Elemente messen dann beim Laden gar nichts.
Skripte, die du nicht ändern kannst
Session-Recorder und Heatmap-Tags lesen Größen absichtlich aus: Microsoft Clarity ermittelt beim Start ein Dutzend Abmessungen der Seite und die Position jedes geklickten Elements. Entferne das Tag, zeichne nur eine Stichprobe der Besuche auf oder lade es nur auf den Seiten, die du analysierst.
Ein Forced Reflow in einem Plugin oder einer App, die du nicht ersetzen kannst? Oder Taps, die auf den Smartphones deiner Besucher hängen, auf deinem aber nicht? In einer Core-Web-Vitals-Power-Hour finden wir das Skript hinter deinen langsamsten Taps und entscheiden gemeinsam, ob wir es beheben, ersetzen oder entfernen.
Performance rutscht ab, sobald keiner hinschaut.
Ich setze Monitoring, Performance Budgets und Prozesse auf. Das ist der Unterschied zwischen einem Fix und einer Lösung.
Reden wir