Forced reflow dans Lighthouse : ce qu'il signifie et le fix

Un script qui modifie la page puis demande une dimension force le navigateur à recalculer le layout immédiatement. Quand cela fige les taps, et quand c'est inoffensif.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-10-26

Que signifie Forced reflow dans Lighthouse ?

Un forced reflow signifie qu'un script a modifié la page puis a demandé une dimension : le navigateur a donc dû recalculer le layout immédiatement. Lighthouse liste ces scripts dans l'insight Forced reflow (« A forced reflow occurs when JavaScript queries geometric properties »). Une fois, c'est normal ; dans une boucle, les taps se figent. Laissez le CSS faire le travail du script, ou lisez toutes les dimensions avant de modifier quoi que ce soit.

Que liste l'avertissement Forced reflow ?

Lighthouse 13 et PageSpeed Insights le décrivent ainsi : « 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. » Il passe au rouge lorsque des scripts forcent 30 ms ou plus de travail de layout d'un coup pendant le chargement de la page.

Le rapport ci-dessous provient d'une page d'annonces de chalets conçue comme un site de location de vacances WordPress, dont le script de thème coupe chaque description à trois lignes et donne la même hauteur à toutes les cartes.

Lighthouse 13.5 report, the Forced reflow insight expanded with the label Unscored: Top function call cabins.js line 46, 388 ms; Source rows cabins.js line 10 3 ms, line 12 733 ms, line 23 7 ms

Lighthouse 13,5 sur mobile, throttling DevTools (Slow 4G, CPU 4x).

  • Top function call : la fonction englobante qui a déclenché le travail de layout. Survolez son chemin de fichier raccourci pour voir le chemin complet, qui indique le thème, le plugin ou l'application. Un fichier situé sur un autre domaine est généralement un tag tiers, comme un enregistreur de session (Scripts que vous ne pouvez pas modifier).
  • Source : chaque ligne de code qui a demandé une dimension.
  • Total reflow time : le temps de layout provoqué par cette fonction ou cette ligne.
  • Unscored : l'insight n'a aucun poids dans le score Performance. Les long tasks sous-jacentes (périodes de plus de 50 ms pendant lesquelles la page ne peut pas répondre) réduisent le score via le Total Blocking Time.

La première ligne (388 ms) est inférieure au cumul des lignes Source (743 ms) parce que le script des chalets s'exécute deux fois pendant le chargement, et la première ligne n'affiche que l'exécution la plus lourde.

Pourquoi un script force-t-il le navigateur à recalculer le layout ?

Sur la liste de chalets ci-dessus, un tap sur le filtre « All » fige la page pendant un tiers de seconde avant que les 48 cartes n'apparaissent.

Le layout est l'étape où le navigateur détermine la position et la taille de chaque boîte. Il ne le fait normalement qu'une fois par frame, après l'exécution des scripts. Lorsqu'un script modifie la page (un texte, une classe, une hauteur) puis lit une dimension, comme offsetHeight, getBoundingClientRect(), scrollTop ou getComputedStyle(), le navigateur doit recalculer le layout immédiatement, au sein du script, pour répondre. Paul Irish maintient la liste complète des lectures qui le forcent.

Un seul forced reflow correspond surtout à un layout que le navigateur aurait effectué un instant plus tard. Dans une boucle qui modifie puis lit à nouveau, c'est un layout complet à chaque itération : du layout thrashing. Le thème des chalets coupe chaque description ainsi :

while (el.offsetHeight > max && words.length > 1) {
  words.pop();
  el.textContent = words.join(' ') + '…';
}

Chaque mot retiré modifie le texte, donc la lecture suivante de offsetHeight force un nouveau layout : un par mot, sur chaque carte.

À quel point un forced reflow ralentit-il une page ?

J'ai mesuré la page des chalets avec le script du thème (Avant) et avec du CSS pour couper le texte et égaliser la hauteur des cartes (Après). Les deux versions ont le même rendu.

AvantAprès
Score Lighthouse8492
Total Blocking Time360 ms0 ms
Total reflow time743 msaucun
Un tap sur « All »320 ms16 ms
Lighthouse report at phone width for the cabin listing before the fix: Performance 84, First Contentful Paint 2.7 s, Largest Contentful Paint 2.7 s, Total Blocking Time 360 ms, CLS 0

Avant

The same report after the fix: Performance 92, First Contentful Paint 2.7 s, Largest Contentful Paint 2.7 s, Total Blocking Time 0 ms, CLS 0

Après

Lighthouse 13,5 sur mobile, throttling DevTools.

Ces 8 points proviennent du Total Blocking Time : les layouts forcés bloquent la page plus longtemps. Le tap est ce que ressent le visiteur, et il compte pour l'INP (Interaction to Next Paint), le Core Web Vital mesurant la rapidité de réponse d'une page aux taps et aux clics. 320 ms dépassent la limite de 200 ms d'un bon INP, dont 298 ms de layout forcé par le script ; 16 ms correspondent à une seule frame. Lighthouse n'effectue jamais de tap ; j'ai donc mesuré les taps dans Chrome avec le CPU ralenti à la vitesse d'un mobile.

Quand un forced reflow est-il inoffensif ?

  • Une seule petite ligne pendant le chargement. Une ligne d'à peine plus de 30 ms correspond souvent au premier layout de la page, déclenché un peu plus tôt parce qu'un script a demandé une dimension. Le script de thème des chalets, réécrit pour lire d'abord toutes les hauteurs, affichait une seule ligne de 37 ms sur l'un des quatre tests Lighthouse, et le tap sur « All » prenait 16 ms.
  • Une ligne « [unattributed] ». Elle ne mentionne aucune ligne de code : relancez Lighthouse et, si la ligne a disparu, ignorez-la.
  • Un script qui lance une animation. Les modales Bootstrap et certains plugins de popup lisent délibérément une dimension pour lancer leur transition CSS.
Forced reflow insight with one row: Top function call cabins.js line 29, 37 ms, and one Source row cabins.js line 8, 37 ms

Lighthouse 13,5 sur mobile, throttling DevTools.

Corrigez le problème quand le temps total de reflow se compte en centaines de millisecondes, qu'un même fichier cumule de nombreuses lignes, ou quand le script tourne aussi lors des taps : un filtre, un menu, un slider.

Quel script provoque le forced reflow lors d'un tap ?

Lighthouse se contente de charger la page : enregistrez les taps qui bloquent dans le panneau Performance :

  1. Ouvrez les DevTools (F12), allez dans Performance et réglez le CPU sur un ralentissement 4x (« 4x slowdown ») pour que votre ordinateur se comporte comme un téléphone.
  2. Cliquez sur Record, faites le tap qui semble lent et arrêtez l'enregistrement.
  3. Ouvrez Forced reflow dans la barre latérale Insights. « Call frames that trigger reflow » indique la fonction et la ligne.
Chrome DevTools Performance panel with one tap recorded: the Main track shows Event: click, filterCards, layoutCards and clampText over a long row of layout work; the Forced reflow insight in the sidebar lists 452 ms and the call frames clampText in cabins.js lines 10 and 12

Un tap sur « All » sur la page des chalets : clampText dans cabins.js.

La console signale la même chose sous la forme « [Violation] Forced reflow while executing JavaScript took 38ms », dès que vous activez le niveau Verbose dans ses filtres de log.

Quand Lighthouse ne mentionne que jquery.min.js, c'est que jQuery a effectué la lecture pour le compte d'un thème ou d'un plugin. Enregistrez un rechargement dans le panneau Performance et cliquez sur un bloc Layout marqué d'un triangle rouge : sous Function stack dans l'onglet Summary, le fichier intercalé entre les lignes de jQuery, avec un chemin comme /wp-content/plugins/<name>/, est le plugin en cause.

Les field data contiennent les taps et les clics de vos visiteurs réels. Dans CoreDash, ouvrez INP, Breakdown et groupez par LOAF : Chrome enregistre les scripts exécutés dans chaque frame lente (Long Animation Frames), de sorte que chaque ligne correspond à un script avec l'INP des interactions concernées. Un forced reflow dans un gestionnaire de clic ou de tap compte comme du processing, la partie violette de chaque barre : commencez donc par un script lent dont la portion violette est longue.

CoreDash INP subparts by LOAF: p75 INP per script, from 125 ms (good) to 0.65 s (poor), each split into input delay, processing and presentation.

INP par script dans CoreDash, desktop, p75 : vert pour l'input delay, violet pour le processing, bleu pour la presentation.

Comment corriger un forced reflow ?

Commencez par le fichier indiqué dans Top function call et demandez-vous si le CSS peut faire le travail de ce script. Un texte limité à trois lignes (line-clamp), des cartes de hauteur égale (grid), un en-tête qui reste fixé en haut (position: sticky) et un conteneur d'image qui conserve ses proportions (aspect-ratio) ne nécessitent aucun script. Et sans script, rien ne force le layout.

Après chaque correction ci-dessous, enregistrez à nouveau le même tap : Forced reflow doit avoir disparu de la barre latérale Insights. Vérifiez ensuite le résultat dans Lighthouse et dans vos field data ; la Search Console met jusqu'à 28 jours à réagir.

Thèmes WordPress, plugins et page builders

Le chemin dans Top function call indique le thème (/wp-content/themes/) ou le plugin (/wp-content/plugins/). Cherchez d'abord un réglage. L'option « Stretch Section » d'Elementor élargit une section en JavaScript à chaque chargement et redimensionnement ; la mise en page Elementor Full Width avec un conteneur pleine largeur le fait en CSS. Un slider Swiper configuré sur « auto » slides per view mesure chaque slide ; avec un nombre fixe, il calcule les largeurs sans les lire.

Retarder le JavaScript dans WP Rocket ou un plugin similaire fait disparaître l'avertissement, mais le script s'exécute alors au premier tap ou scroll du visiteur, avec les mêmes layouts forcés. Corrigez plutôt le script, ou signalez-le au développeur du plugin et remplacez le plugin si rien ne change.

Quand le thème coupe le texte en JavaScript, coupez-le en CSS dans votre thème enfant, avec la classe de texte de votre thème :

.cabin .desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

Désactivez ensuite le script de clamp du thème : via une option du thème, ou avec wp_dequeue_script() et son handle.

Thèmes et applications Shopify

Dans l'éditeur de thème, sous App embeds, désactivez les applications une par une et relancez Lighthouse. Dès que l'avertissement disparaît, signalez l'application à son développeur ou remplacez-la. Un chemin contenant /assets/ dans Top function call correspond à un fichier de thème, issu du thème lui-même ou d'une application qui y a injecté du code. Dawn et les thèmes dérivés lisent généralement une dimension une seule fois et regroupent leurs modifications : inspectez donc les applications en premier.

Votre propre code JavaScript et React

Quand un script doit mesurer, lisez d'abord toutes les dimensions puis appliquez les modifications, afin qu'un seul layout réponde à chaque lecture. Exemple avec un accordéon qui ajuste chaque panneau à sa propre hauteur :

const panels = [...document.querySelectorAll('.panel')];

// Un layout pour chaque panneau : lecture, écriture, lecture, écriture
panels.forEach(p => { p.style.maxHeight = p.scrollHeight + 'px'; });

// Un seul layout : lire toutes les hauteurs, puis les écrire toutes
const heights = panels.map(p => p.scrollHeight);
panels.forEach((p, i) => { p.style.maxHeight = heights[i] + 'px'; });
  • Resize et scroll : ResizeObserver et IntersectionObserver fournissent les dimensions issues du layout que le navigateur vient d'effectuer. Utilisez-les au lieu de lire des dimensions dans un gestionnaire de resize ou de scroll.
  • React : mesurez une seule fois dans useLayoutEffect et mettez à jour le state une seule fois. Ne montez les menus, popovers et tooltips qu'à leur ouverture, afin que les éléments fermés ne mesurent rien pendant le chargement.

Scripts que vous ne pouvez pas modifier

Les enregistreurs de session et les tags de heatmap lisent délibérément des dimensions : Microsoft Clarity lit une douzaine de dimensions de page à son initialisation, ainsi que la position de chaque clic du visiteur. Supprimez le tag, enregistrez un échantillon de visites ou chargez-le uniquement sur les pages que vous analysez.

Un forced reflow dans un plugin ou une application impossible à remplacer, ou des taps qui bloquent sur le téléphone de vos visiteurs et pas sur le vôtre ? Lors d'une Core Web Vitals power hour, nous identifions le script responsable de vos taps les plus lents et décidons ensemble s'il faut le corriger, le remplacer ou le supprimer.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

 

CoreDash intègre MCP nativement.

Branchez-le à Claude ou à n'importe quel AI agent. Demandez-lui pourquoi votre INP s'est envolé mardi dernier.

Voyez comment ça marche
Forced reflow dans Lighthouse : ce qu'il signifie et le fix Core Web Vitals Forced reflow dans Lighthouse : ce qu'il signifie et le fix