Part 22: useLayoutEffect Hook: Synchronous DOM Updates & Measurements
useLayoutEffect is identical to useEffect in signature, but it fires synchronously after all DOM mutations, before the browser paints pixels to the screen.
1. The Rendering Lifecycle: useEffect vs useLayoutEffect
[ State / Props Change ]
│
▼
[ React renders JSX & updates Virtual DOM ]
│
▼
[ React mutates Real DOM nodes ]
│
├──► [ useLayoutEffect runs SYNCHRONOUSLY ] (Blocks Paint)
│
▼
[ Browser Paints Screen (User sees new UI) ]
│
├──► [ useEffect runs ASYNCHRONOUSLY ] (Does NOT Block Paint)
2. When to Use useLayoutEffect
Use useLayoutEffect when you need to:
1. Measure DOM elements (e.g., width, height, scroll position).
2. Apply DOM adjustments based on those measurements before the browser paints, preventing visual flickering.
Real-World Example: Tooltip Positioning
import { useState, useRef, useLayoutEffect } from 'react';
export default function Tooltip({ targetRect, text }) {
const tooltipRef = useRef(null);
const [coords, setCoords] = useState({ top: 0, left: 0 });
useLayoutEffect(() => {
if (tooltipRef.current) {
const tooltipHeight = tooltipRef.current.offsetHeight;
// Position tooltip right above target without visual flicker
setCoords({
top: targetRect.top - tooltipHeight - 8,
left: targetRect.left
});
}
}, [targetRect]);
return (
<div
ref={tooltipRef}
style={{ position: 'fixed', top: coords.top, left: coords.left }}
className="bg-black text-white px-2 py-1 rounded text-xs"
>
{text}
</div>
);
}
Key Takeaways
useEffectis non-blocking and preferred for 99% of side effects (data fetching, timers).useLayoutEffectruns synchronously before paint and is reserved for layout measurements to prevent visual flickers.
In Part 23, we explore advanced hooks: useId and useImperativeHandle!

