Forced reflow no Lighthouse: o que significa e como corrigir

Um script que altera a página e pede de seguida uma dimensão faz o browser refazer o layout de imediato. Quando isso faz os toques encravar e quando é inofensivo.

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

O que significa Forced reflow no Lighthouse?

Um forced reflow significa que um script alterou a página e pediu de seguida uma dimensão, obrigando o browser a recalcular o layout de imediato. O Lighthouse lista esses scripts no insight Forced reflow ("Um reflow forçado ocorre quando o JavaScript consulta propriedades geométricas"). Uma vez é normal; num ciclo, faz os toques encravar. Deixe o CSS fazer o trabalho do script, ou leia todas as dimensões antes de alterar o que quer que seja.

O que lista o aviso de Forced reflow?

O Lighthouse 13 e o PageSpeed Insights descrevem-no assim: "Um reflow forçado ocorre quando o JavaScript consulta propriedades geométricas (como offsetWidth) após os estilos terem sido invalidados por uma alteração ao estado do DOM. Isto pode resultar num fraco desempenho. Saiba mais sobre reflows forçados e possíveis mitigações." Fica vermelho quando os scripts forçam 30 ms ou mais de trabalho de layout de uma só vez durante o carregamento da página.

O relatório abaixo vem de uma página de listagem de cabanas que criei à semelhança de um site WordPress de alojamento de férias, cujo script do tema corta cada descrição para três linhas e deixa todos os cartões com a mesma altura.

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 no telemóvel, throttling do DevTools (Slow 4G, 4x CPU).

  • Top function call: a função exterior que iniciou o trabalho de layout. Passe o cursor sobre o caminho encurtado do ficheiro para ver o caminho completo, que identifica o tema, plugin ou app. Um ficheiro noutro domínio costuma ser uma tag de terceiros, como um gravador de sessões (Scripts que não pode alterar).
  • Source: cada linha de código que pediu uma dimensão.
  • Total reflow time: o tempo de layout causado por essa função ou linha.
  • Unscored: o insight não tem peso na pontuação de Performance. As long tasks associadas (períodos superiores a 50 ms em que a página não consegue responder) descem a pontuação através do Total Blocking Time.

A linha superior (388 ms) é inferior à soma das linhas de Source (743 ms) porque o script das cabanas é executado duas vezes durante o carregamento, e a linha superior mostra apenas a execução maior.

Porque é que um script força o browser a refazer o layout?

Na listagem de cabanas acima, um toque no filtro "All" congela a página durante um terço de segundo antes de surgirem os 48 cartões.

O layout é a etapa em que o browser calcula a posição e o tamanho de cada caixa. Normalmente faz isso uma vez por frame, depois de os scripts correrem. Quando um script altera a página (um texto, uma classe, uma altura) e a seguir lê uma dimensão, como offsetHeight, getBoundingClientRect(), scrollTop ou getComputedStyle(), o browser tem de calcular o layout de imediato, dentro do script, para responder. O Paul Irish mantém a lista completa das leituras que o forçam.

Um único forced reflow é, quase sempre, trabalho de layout que o browser faria instantes depois. Num ciclo que altera e volta a ler, passa a ser um layout completo em cada iteração: layout thrashing. O tema das cabanas corta cada descrição desta forma:

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

Cada palavra removida altera o texto, pelo que a leitura seguinte de offsetHeight força um novo layout: um por palavra, em cada cartão.

Quanto é que um forced reflow atrasa uma página?

Medi a listagem de cabanas com o script do tema (Antes) e com CSS a cortar o texto e a igualar a altura dos cartões (Depois). Ambas as versões têm o mesmo aspeto visual.

AntesDepois
Pontuação do Lighthouse8492
Total Blocking Time360 ms0 ms
Total reflow time743 msnenhum
Um toque em "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

Antes

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

Depois

Lighthouse 13.5 no telemóvel, throttling do DevTools.

Os 8 pontos vêm do Total Blocking Time: os layouts forçados mantêm a página bloqueada durante mais tempo. O toque é o que o visitante sente, e conta para o INP (Interaction to Next Paint), a Core Web Vital que mede a rapidez com que a página responde a toques e cliques. 320 ms ultrapassa o limite de 200 ms para um bom INP, e 298 ms desse tempo foram layout forçado pelo script; 16 ms correspondem a uma única frame. O Lighthouse nunca simula toques, pelo que medi os toques no Chrome com o CPU abrandado para a velocidade de um telemóvel.

Quando é que um forced reflow é inofensivo?

  • Uma única linha pequena durante o carregamento. Uma linha ligeiramente acima de 30 ms é, muitas vezes, o primeiro layout da página, antecipado porque um script pediu uma dimensão. O script do tema da listagem de cabanas, reescrito para ler primeiro todas as alturas, apresentou uma linha de 37 ms numa em cada quatro execuções do Lighthouse, e o toque em "All" demorou 16 ms.
  • Uma linha "[unattributed]". Não indica nenhuma linha de código: execute o Lighthouse novamente e, se a linha desaparecer, ignore.
  • Um script que inicia uma animação. Os modais do Bootstrap e alguns plugins de popups leem uma dimensão de propósito para que a transição CSS arranque.
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 no telemóvel, throttling do DevTools.

Corrija quando o tempo total de reflow estiver na ordem das centenas de milissegundos, quando um ficheiro tiver muitas linhas, ou quando o script também for executado em toques: um filtro, um menu, um slider.

Que script causa o forced reflow num toque?

O Lighthouse apenas carrega a página, por isso grave no painel Performance os toques que encravam:

  1. Abra o DevTools (F12), vá a Performance e defina o abrandamento do CPU para 4x, para que o portátil se comporte como um telemóvel.
  2. Clique em Record, toque no elemento que parece lento e pare a gravação.
  3. Abra Forced reflow na barra lateral de Insights. "Call frames that trigger reflow" indica a função e a linha.
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

Um toque em "All" na listagem de cabanas: clampText em cabins.js.

A consola reporta o mesmo através de "[Violation] Forced reflow while executing JavaScript took 38ms", assim que ativar Verbose nos níveis de registo.

Quando o Lighthouse apenas indica jquery.min.js, foi o jQuery que fez a leitura em nome de um tema ou plugin. Grave um recarregamento no painel Performance e clique num bloco de Layout com um triângulo vermelho: no separador Summary, por baixo de Function stack, o ficheiro entre as linhas do jQuery, com um caminho como /wp-content/plugins/<name>/, é o plugin.

Os seus field data contêm os toques e cliques dos visitantes reais. No CoreDash, abra INP, Breakdown e agrupe por LOAF: o Chrome regista que scripts correram em cada frame lenta (Long Animation Frames), pelo que cada linha representa um script com o INP das interações em que participou. Um forced reflow no handler de um clique ou toque conta como processing, a parte roxa de cada barra; comece, por isso, por um script lento cuja parte roxa seja longa.

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 por script no CoreDash, desktop, p75: verde é input delay, roxo processing, azul presentation.

Como corrigir um forced reflow?

Comece pelo ficheiro em Top function call e veja se o CSS pode fazer o trabalho desse script. Texto cortado a três linhas (line-clamp), cartões com a mesma altura (grid), um cabeçalho fixo no topo (position: sticky) e uma caixa de imagem que mantém as proporções (aspect-ratio) não precisam de scripts; e sem o script, nada força o layout.

Depois de aplicar qualquer uma das correções abaixo, volte a gravar o mesmo toque: o Forced reflow deverá ter desaparecido da barra lateral de Insights. Em seguida, verifique no Lighthouse e nos seus field data; a Search Console demora até 28 dias.

Temas, plugins e page builders do WordPress

O caminho em Top function call indica o tema (/wp-content/themes/) ou o plugin (/wp-content/plugins/). Procure primeiro por uma opção nas definições. A opção "Stretch Section" do Elementor alarga uma secção com JavaScript em cada carregamento e redimensionamento; o layout de página Elementor Full Width com um contentor de largura total faz o mesmo em CSS. Um slider Swiper configurado com "auto" em slides per view mede cada slide; com um número fixo, calcula as larguras sem ter de as ler.

Adiar o JavaScript no WP Rocket ou num plugin semelhante limpa o aviso, mas o script passa a correr no primeiro toque ou scroll do visitante, com os mesmos layouts forçados. Em vez disso, corrija o script ou reporte-o ao programador do plugin e substitua o plugin se nada mudar.

Quando o tema corta texto com JavaScript, corte-o com CSS no seu child theme, usando a classe do tema para o texto:

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

Depois, desative o script de clamp do tema: através de uma opção do tema, ou com wp_dequeue_script() usando o respetivo handle.

Temas e apps Shopify

No editor do tema, em App embeds, desative as apps uma a uma e execute novamente o Lighthouse. Quando o aviso desaparecer, reporte essa app ao respetivo programador ou substitua-a. Um caminho com /assets/ em Top function call é um ficheiro do tema, seja do próprio tema ou de uma app que lhe adicionou código. O Dawn e os temas nele baseados normalmente leem a dimensão uma só vez e agrupam as alterações em lote; comece, por isso, por analisar as apps.

JavaScript próprio e React

Quando um script tem mesmo de medir, leia primeiro todas as dimensões e faça as alterações depois, para que um único layout responda a todas as leituras. Um acordeão que define a altura própria de cada painel:

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

// Um layout por painel: ler, escrever, ler, escrever
panels.forEach(p => { p.style.maxHeight = p.scrollHeight + 'px'; });

// Um só layout: ler todas as alturas, depois escrever todas
const heights = panels.map(p => p.scrollHeight);
panels.forEach((p, i) => { p.style.maxHeight = heights[i] + 'px'; });
  • Resize e scroll: o ResizeObserver e o IntersectionObserver fornecem as dimensões do layout que o browser acabou de calcular. Utilize-os em vez de ler dimensões num handler de resize ou scroll.
  • React: meça uma vez em useLayoutEffect e atualize o estado uma só vez. Monte menus, popovers e tooltips apenas quando abrem, para que os elementos fechados não meçam nada durante o carregamento.

Scripts que não pode alterar

Os gravadores de sessões e as tags de mapas de calor leem dimensões de propósito: o Microsoft Clarity lê uma dúzia de dimensões da página ao arrancar e a posição de cada elemento em que o visitante clica. Remova a tag, grave apenas uma amostra das visitas ou carregue-a unicamente nas páginas que estiver a analisar.

Tem um forced reflow num plugin ou app que não pode substituir, ou toques que encravam nos telemóveis dos visitantes e não no seu? Numa power hour de Core Web Vitals, identificamos o script por trás dos toques mais lentos e decidimos em conjunto se a solução é corrigir, substituir ou remover.

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.

 

Ponho sites a passar os Core Web Vitals.

Mais de 500K páginas para grandes editoras europeias e e-commerce. Escrevo os fixes e confirmo-os com dados RUM.

Como trabalho
Forced reflow no Lighthouse: o que significa e como corrigir Core Web Vitals Forced reflow no Lighthouse: o que significa e como corrigir