useEffect vs useLayoutEffect no React: quando usar, exemplos e armadilhas
Por John Pires · 13 de março de 2026

Contexto: quando você vai usar isso
Por que existem efeitos em componentes React
Hooks como useEffect e useLayoutEffect servem para executar código que precisa rodar depois que o React atualiza o DOM. Eles resolvem tarefas que não são puras: ler ou escrever no DOM, iniciar timers, configurar assinaturas (sockets, ouvintes), ou sincronizar valores com APIs externas. Em componentes funcionais, não há métodos de ciclo de vida tradicionais; os effects são a forma padrão de reagir a mudanças.
Para quem está começando: pense no render como a construção de um esqueleto virtual. Os effects são os passos que você faz depois da montagem para ajustar detalhes reais no navegador.
Diferença principal entre useEffect e useLayoutEffect
Resumo prático: useEffect roda após o paint no navegador (assíncrono) e não bloqueia a renderização. useLayoutEffect roda de forma síncrona depois que o DOM foi atualizado mas antes do browser pintar — ele bloqueia até terminar. Isso faz do useLayoutEffect a ferramenta certa para leituras de layout e ajustamentos que, se feitos assíncronamente, causariam um deslocamento visível (layout shift).
Importante para iniciantes: prefira useEffect na maioria dos casos. Use useLayoutEffect somente quando você precisa medir o DOM e aplicar mudanças antes do paint.
Explicação visual com diagramas mentais
Linha do tempo: render -> commit -> paint
Imagine o ciclo do React assim:
// Simplificação do ciclo de vida (timeline)
1. Render (virtual DOM atualizado) // função componente executa
2. Commit (aplica mudanças no DOM real) // DOM real recebe as mutações
3. useLayoutEffect (síncrono) // roda antes do paint
4. Paint (navegador desenha pixels)
5. useEffect (assíncrono) // roda depois do paint
Se você precisa ler dimensões do DOM e ajustar CSS antes do paint, escolha useLayoutEffect. Caso contrário, use useEffect para não bloquear a renderização.
Analogia: montagem de móveis
Pense numa montagem de móveis: fazer o frame é o render. Colocar os parafusos críticos antes de colocar o acabamento é o useLayoutEffect (sincrono). Passar a lixa e o verniz depois que o móvel já está no lugar é o useEffect (assíncrono). Se você lixar depois do verniz, estraga; se você faz tudo no caminho crítico, atrasa a entrega.
Regra prática: bloquear o fluxo grande demais atrasa a interface. Use o sincronismo só quando necessário.
Primeiro exemplo simples
Contador que atualiza o título da página
Contexto: um contador que atualiza o document.title. Isso não precisa bloquear o paint, portanto useEffect é apropriado. O exemplo abaixo mostra o comportamento correto e uma versão com useLayoutEffect que funciona, mas é desnecessária aqui.
// O que faz: atualiza document.title com o contador
import React, { useState, useEffect, useLayoutEffect } from 'react';
export function CounterUseEffect() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Contador: ${count}`;
}, [count]);
return (
Contador: {count}
setCount(c => c + 1)}>+
);
}O que acontece: o navegador atualiza a UI primeiro; depois o effect roda e altera o título da aba. O usuário não nota nenhuma lentidão porque a atualização do title é rápida e fora do caminho crítico do paint.
Por que não usar useLayoutEffect aqui
Se você trocar por useLayoutEffect o código funciona do mesmo jeito funcionalmente, mas passa a bloquear o paint até que o effect termine. Em cenários simples isso não é perceptível, mas em componentes com muitos cálculos ou em dispositivos lentos, você cria potenciais travamentos.
// Versão desnecessária: bloqueante para uma tarefa não crítica
import React, { useState, useLayoutEffect } from 'react';
export function CounterUseLayoutEffect() {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
document.title = `Contador: ${count}`;
}, [count]);
return (
Contador: {count} setCount(c => c + 1)}>+
);
}
O que acontece: comportamentalmente idêntico, porém você sacrifica performance sem necessidade. Portanto: use useEffect por padrão.
Segundo exemplo mais realista
Medindo um elemento e evitando layout shift
Contexto: você precisa medir a largura de uma caixa logo após ela ser renderizada e, com base nisso, aplicar uma correção de estilo que evita um 'salto' visual. Aqui useLayoutEffect é a escolha correta porque a leitura e escrita do DOM precisam ocorrer antes do paint.
// O que faz: mede a largura de um elemento e aplica uma classe de ajuste
import React, { useRef, useState, useLayoutEffect } from 'react';
export function MeasuredBox() {
const ref = useRef(null);
const [wide, setWide] = useState(false);
useLayoutEffect(() => {
if (!ref.current) return;
const w = ref.current.getBoundingClientRect().width;
// Se for maior que 300px, ativa um ajuste (evita reflow visível)
setWide(w > 300);
}, []);
return (
Caixa medida — largura atual aplicada antes do paint
);
}
O que acontece: ao renderizar, o React aplica o DOM, entra no commit e executa o useLayoutEffect. O efeito mede a largura e ajusta o estado antes do browser pintar. Assim, a cor/estilo final é aplicado sem que o usuário veja um estado intermediário.
Versão com useEffect (o problema)
Se a mesma lógica usar useEffect, o paint acontece primeiro com o estado inicial. Só depois a medida é feita e o estado atualiza, causando um re-paint perceptível — o famoso layout shift.
// O que faz: mesma medição, porém usando useEffect -> causa layout shift
import React, { useRef, useState, useEffect } from 'react';
export function MeasuredBoxWithShift() {
const ref = useRef(null);
const [wide, setWide] = useState(false);
useEffect(() => {
if (!ref.current) return;
const w = ref.current.getBoundingClientRect().width;
setWide(w > 300);
}, []);
return (
Caixa medida — pode causar layout shift
);
}O que acontece: visualmente, o usuário pode ver a caixa em uma cor (estado inicial) e, em seguida, o repaint altera a cor. Em UIs sensíveis a estabilidade visual, esse comportamento é indesejável.
Erros comuns de iniciante e como evitar
Esquecer dependências ou listá-las incorretamente
Muitos iniciantes omitem dependências do array do effect ou colocam funções geradas inline, causando efeitos que não disparam quando deveriam ou disparos infinitos. Regras práticas:
Inclua tudo que você usa dentro do effect (props, state, refs, funções externas).
Se a função muda a cada render, use
useCallbackou mova a lógica para dentro do effect.Evite desabilitar o lint (eslint-plugin-react-hooks) sem entender o risco.
Exemplo de erro comum: criar uma função dentro do componente e colocá-la no array de dependências sem memoizar — isso aciona o effect toda render.
// O que faz: exemplo com loop infinito
import React, { useState, useLayoutEffect } from 'react';
export function BadLoop() {
const [count, setCount] = useState(0);
const doSomething = () => {
// função recriada a cada render
return count + 1;
};
useLayoutEffect(() => {
// depende de doSomething; como ela muda a cada render, o effect repete
const next = doSomething();
setCount(next);
}, [doSomething]);
return {count};
}Correção: remova a função das dependências ou memoize-a com useCallback, e prefira usar o padrão do setState com função quando possível.
Bloquear o thread com lógica pesada em useLayoutEffect
Recurso perigoso: colocar cálculos pesados dentro de useLayoutEffect. Como ele é síncrono no commit, tudo que rodar ali atrasa o paint e deixa a UI travada. Se precisar computar muito, mova para Web Worker, memoize, ou use useEffect com técnicas para reduzir flashes visuais.
Lista de armadilhas:
Executar loops demorados em
useLayoutEffect.Fazer chamadas de rede longas no
useLayoutEffect(useEffect é melhor).Misturar manipulação direta do DOM e atualizações de estado sem checar se o componente ainda está montado.
Prática segura: se precisar medir e também buscar dados, meça com useLayoutEffect e faça a fetch com useEffect. Separe responsabilidades.
Conclusão prática
Regras rápidas para escolher
Por padrão, use useEffect.
Se você precisa ler layout (getBoundingClientRect) e mudar o DOM ou estado para evitar um layout shift, use useLayoutEffect.
Evite lógicas pesadas em useLayoutEffect; prefira otimizar o algoritmo ou usar workers.
Mantenha o array de dependências correto para evitar efeitos incorretos ou loops.
Checklist antes de escolher
Antes de trocar para useLayoutEffect, responda:
Preciso medir o DOM e aplicar a mudança antes do paint?
A mudança evita um salto visual perceptível?
O trabalho no effect é rápido e seguro de rodar síncronamente?
Se a resposta for sim às três, use useLayoutEffect. Caso contrário, use useEffect.
Dica final: começar por
useEffecte só migrar parauseLayoutEffectquando você tiver um problema claro de layout é uma abordagem pragmática e segura.
Com esses exemplos e regras você tem uma base prática para decidir entre os dois hooks. Experimente os exemplos no seu ambiente, observe o comportamento visual e use ferramentas como o profiler do React para medir impacto de performance.
Boa prática: sempre documente por que um effect é useLayoutEffect no código — isso ajuda futuros mantenedores a entenderem a decisão.
Assuntos: React, hooks, uselayouteffect, useeffect, Performance
Leia também
Context selectors: reduzir re-renders e otimizar estado global no React
Context selectors reduzem re-renders no React ao permitir que componentes leiam apenas fatias do estado global. Este artigo mostra quando usar, compara…
Corrigindo mismatches de hydration no React App Router (SSR): passo a passo
Guia prático e direto para identificar e corrigir mismatches de hydration no React App Router (SSR). Passo a passo: ambiente, exemplos, mini-projeto com…
Criando useStableCallback (useEvent): callbacks estáveis no React
Aprenda a criar useStableCallback, um hook que fornece callbacks com identidade estável mas que chamam a versão mais recente do handler. Mostro o erro comum…
Suavizando transições em listas React com startTransition, useDeferredValue e Suspense
Como evitar travamentos em listas React: use startTransition para priorizar atualizações, useDeferredValue para adiar derivação de valores, e Suspense para…
Sincronizar estado entre Server e Client Components com use e useSyncExternalStore
Padronize estado entre Server e Client Components usando use (servidor) e useSyncExternalStore (cliente). Evite fetch duplicado, hydrate sem mismatch e…
Melhorar LCP com React Server Components e Suspense: streaming, placeholders e revalidação
Reduza LCP com React Server Components e Suspense: prioridades de dados, streaming incremental, placeholders que preservam layout e revalidação de cache…