useLayoutEffect es una versión de useEffect que se ejecuta antes: justo después de que React actualizó el DOM, pero antes de que el navegador pinte el nuevo frame. El código que contiene puede medir elementos y cambiar el layout, y el usuario solo ve el resultado final. Bloquea el pintado mientras se ejecuta, así que úsalo solo cuando necesites ese momento.
Los efectos se declaran en el orden efecto, layout effect, pero la consola imprime primero el layout effect en cada renderizado. El orden de declaración no importa entre los dos hooks; importa su momento.
La sintaxis
La firma es idéntica a la de useEffect: una función de configuración, una limpieza opcional y un array de dependencias.
useLayoutEffect(() => {
// runs after the DOM update, before paint
return () => {
// cleanup, before the next run and on unmount
};
}, [dependencies]);
Todo lo que sabes sobre dependencias y limpieza de useEffect se aplica. La única diferencia es cuándo lo llama React.
Cuándo se ejecuta cada uno
Después de un renderizado, React actualiza el DOM y luego hace tres cosas en un orden fijo:
- Ejecuta todas las limpiezas de los layout effects y los layout effects, de forma síncrona.
- Deja que el navegador pinte. Si un layout effect cambió el estado, React primero vuelve a renderizar, todavía antes del pintado.
- Ejecuta los efectos normales, normalmente después del pintado.
En todo el árbol, cada layout effect se ejecuta antes que cualquier efecto normal, y los hijos se ejecutan antes que sus padres, porque un padre solo puede terminar cuando sus hijos están en su lugar:
La consola muestra primero ambos layout effects, Child y luego Parent, y después ambos efectos normales en el mismo orden. El key={n} hace que cada clic monte un Parent nuevo, así que obtienes las mismas cuatro líneas cada vez. Agrega a cada efecto una limpieza que registre y mira cómo los viejos se ejecutan antes que los nuevos.
El problema del parpadeo
Imagina un tooltip que debe aparecer encima de su botón, a menos que no haya espacio arriba, en cuyo caso va debajo. No puedes saber si hay espacio hasta que el tooltip está en el DOM y puedes medir su altura. Así que el renderizado ocurre en dos pasadas:
- Renderizar el tooltip en algún lugar, para poder medirlo.
- Medirlo, calcular la posición real, cambiar el estado, renderizar otra vez.
Con useEffect, el navegador puede pintar entre esas pasadas, y el usuario ve el tooltip saltar de la primera posición a la segunda. Con useLayoutEffect, la segunda pasada ocurre antes del pintado, así que la primera posición nunca se muestra.
Pasa el cursor sobre el botón de arriba: no hay espacio encima, así que el tooltip aparece debajo. Pasa sobre el de abajo y aparece encima. La consola te dice qué lado eligió la medición. Cambia useLayoutEffect por useEffect y vuelve a pasar el cursor: ahora el tooltip puede aparecer un frame en la parte superior de la caja antes de moverse. En una máquina rápida suele ser demasiado rápido para notarlo, y justo por eso este error se publica y luego lo reportan desde teléfonos más lentos.
offsetHeight, offsetTop y getBoundingClientRect() obligan al navegador a calcular el layout, así que leerlos en el layout effect te da los valores reales del DOM que React acaba de escribir.
Medir un elemento
El mismo patrón sirve para cualquier tamaño del que dependa el renderizado: léelo con una ref en un layout effect, guárdalo en el estado y renderiza con él.
Haz clic en el botón y la línea del ancho se actualiza en el mismo frame que el texto. La página nunca muestra un texto nuevo junto a un ancho viejo, porque el segundo renderizado ocurre antes del pintado. Quita [text] del array de dependencias y la medición se ejecuta después de cada renderizado, incluido el que provoca su propio setWidth; se detiene ahí solo porque el ancho ya no cambia.
Prefiere useEffect
Un layout effect retiene el pintado hasta que termina, y si cambia el estado, hasta que también termina el renderizado extra. Uno lento hace que cada actualización de la página se sienta lenta. Usa useLayoutEffect solo cuando se cumple todo esto:
- El efecto lee el layout (tamaño, posición, scroll).
- Cambia lo que se renderiza según esa lectura.
- El estado intermedio sería visible y se vería mal.
Todo lo demás (obtener datos, suscripciones, temporizadores, registros, analítica) va en useEffect. Una regla rápida: empieza con useEffect y cambia solo si puedes ver un parpadeo.
useEffect normalmente se ejecuta después del pintado, pero no siempre. Cuando la actualización viene de una acción discreta del usuario, como un clic o una tecla, React puede ejecutar los efectos antes de que el navegador pinte. Esa es una de las razones por las que un parpadeo puede aparecer en una situación y no en otra.
Renderizado en el servidor
Los efectos nunca se ejecutan durante el renderizado en el servidor, y useLayoutEffect tampoco. El servidor envía el HTML del primer renderizado, y el layout effect se ejecuta solo después de la hidratación en el navegador. React 18 y anteriores advertían "useLayoutEffect does nothing on the server" cuando un componente así se renderizaba en el servidor; React 19 quitó esa advertencia.
Si el primer frame renderizado en el servidor se vería mal sin la medición, hay dos soluciones habituales: renderizar un fallback neutro (por ejemplo, ocultar el tooltip hasta medirlo), o renderizar el componente solo en el cliente:
function ClientOnly({ children }) {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return mounted ? children : null;
}
useInsertionEffect
Hay un tercer hook de efectos, useInsertionEffect, que se ejecuta antes que cualquier layout effect. Existe para las librerías CSS-in-JS que generan etiquetas <style> en tiempo de ejecución: insertar los estilos ahí significa que un layout effect que mide un elemento ya lo ve con estilo. No puede actualizar el estado y las refs todavía no están conectadas, así que no sirve en el código de una aplicación. Si no estás escribiendo una librería de estilos, no lo vas a necesitar.
Preguntas frecuentes
¿Cuál es la diferencia entre useEffect y useLayoutEffect?
El momento. useLayoutEffect se ejecuta de forma síncrona después de que React cambió el DOM pero antes de que el navegador pinte, así que sus actualizaciones de estado se aplican antes de que el usuario vea nada. useEffect normalmente se ejecuta después del pintado. La API y el array de dependencias son los mismos.
¿Cuándo debo usar useLayoutEffect?
Cuando un efecto lee el layout (un tamaño o una posición) y cambia de inmediato lo que se renderiza según esa lectura, como colocar un tooltip encima o debajo de su botón. Sin él, el usuario puede ver durante un frame la primera posición, la equivocada.
¿useLayoutEffect es malo para el rendimiento?
Impide que el navegador pinte hasta que termina, junto con cualquier renderizado que provoque. Un layout effect lento hace que toda la página se sienta lenta, así que usa useEffect a menos que necesites evitar un parpadeo visible.
¿useLayoutEffect se ejecuta en el servidor?
No. Ningún efecto se ejecuta durante el renderizado en el servidor. Las versiones anteriores de React imprimían una advertencia cuando un componente con useLayoutEffect se renderizaba en el servidor; React 19 ya no lo hace, pero el efecto sigue ejecutándose solo en el navegador.