React useLayoutEffect — Measuring DOM Before Paint
Learn when to use useLayoutEffect instead of useEffect for DOM measurements. Covers tooltip positioning, scroll restoration, animation triggers, and preventing visual flicker.
Detailed Explanation
DOM Measurement with useLayoutEffect
useLayoutEffect runs synchronously after DOM mutations but before the browser paints. This makes it the right choice when you need to measure or modify the DOM before the user sees the update.
useEffect vs useLayoutEffect Timeline
Render -> DOM mutation -> useLayoutEffect -> Browser paint -> useEffect
Tooltip Positioning
function Tooltip({ targetRect, text }: {
targetRect: DOMRect;
text: string;
}) {
const tooltipRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState({ x: 0, y: 0 });
useLayoutEffect(() => {
if (!tooltipRef.current) return;
const tooltipRect = tooltipRef.current.getBoundingClientRect();
setPosition({
x: targetRect.left + (targetRect.width - tooltipRect.width) / 2,
y: targetRect.top - tooltipRect.height - 8,
});
}, [targetRect]);
return (
<div ref={tooltipRef} style={{
position: "fixed",
left: position.x,
top: position.y,
}}>
{text}
</div>
);
}
Why useEffect Would Flicker
With useEffect, the tooltip would first render at (0, 0), the browser would paint it there, and only then would the position be corrected -- causing a visible flicker. useLayoutEffect prevents this by updating the position before paint.
Scroll Restoration
useLayoutEffect(() => {
const savedPosition = sessionStorage.getItem("scrollPos");
if (savedPosition) {
window.scrollTo(0, parseInt(savedPosition, 10));
}
}, []);
Performance Warning
useLayoutEffect blocks the browser from painting. If your effect is slow, the user will see a frozen screen. Only use it when you genuinely need to prevent visual flicker. For most side effects, useEffect is the correct choice.
Use Case
Use useLayoutEffect for tooltip and popover positioning, preventing flicker when adjusting layout, scroll position restoration, measuring elements for animations, and any DOM operation that must complete before the user sees the frame.