useLayoutEffect vs useEffect
useLayoutEffect has the exact same API as useEffect, but fires at a different time: synchronously, immediately after React updates the DOM, but before the browser paints anything on screen. useEffect, by contrast, fires asynchronously after the paint.
Timing Comparison
| Hook | Fires | Blocks Paint? |
|---|---|---|
| useEffect | After the browser has painted the update | No |
| useLayoutEffect | Synchronously, right after DOM mutations, before paint | Yes |
The Problem It Solves: Visual Flicker
If an effect needs to measure the DOM and then immediately adjust it (like positioning a tooltip based on its own rendered size), doing that in useEffect can cause a visible flash — the browser paints the "wrong" position first, then the effect runs and corrects it a moment later. useLayoutEffect runs before that first paint, so the correction is invisible.
Measuring and Adjusting Before Paint
import { useLayoutEffect, useRef, useState } from "react";
function Tooltip({ text }) {
const ref = useRef(null);
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setWidth(rect.width); // measured and corrected before the browser paints
}, [text]);
return (
<div ref={ref} style={{ marginLeft: -width / 2 }}>
{text}
</div>
);
}useLayoutEffect Blocks Visual Updates
Because it runs synchronously before paint, a slow useLayoutEffect delays the browser from showing anything at all — overusing it can make an app feel less responsive than using useEffect would.
useLayoutEffect Doesn’t Run on the Server
Like useEffect, useLayoutEffect never runs during server-side rendering — only in the browser. React logs a warning if it’s used in a server-rendered component tree without special handling, since there’s no DOM to measure on the server.
When to Use Which
| Situation | Hook |
|---|---|
| Fetching data, subscriptions, logging, most side effects | useEffect |
| Measuring a DOM node and synchronously adjusting layout to avoid flicker | useLayoutEffect |
Best Practice
Default to useEffect for almost everything. Reach for useLayoutEffect only when you’ve specifically noticed a visual flicker caused by a DOM measurement/adjustment happening after paint.