useLayoutEffect is identical to useEffect, but it fires synchronously after all DOM mutations and before the browser paints.
When to Use It
Use useLayoutEffect when you need to:
- Measure DOM elements before the user sees them
- Prevent visual flicker by making synchronous updates
- Position elements based on other elements' dimensions
Example: Tooltip Positioning
tsxfunction Tooltip({ targetRef, children }: TooltipProps) { const tooltipRef = useRef<HTMLDivElement>(null); const [position, setPosition] = useState({ top: 0, left: 0 }); useLayoutEffect(() => { if (!targetRef.current || !tooltipRef.current) return; const targetRect = targetRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); setPosition({ top: targetRect.bottom + 8, left: targetRect.left + (targetRect.width - tooltipRect.width) / 2 }); }, [targetRef]); return ( <div ref={tooltipRef} style={{ position: 'fixed', top: position.top, left: position.left }} > {children} </div> ); }
useEffect vs useLayoutEffect
Render → DOM Update → useLayoutEffect → Browser Paint → useEffect
| Aspect | useEffect | useLayoutEffect |
|---|---|---|
| Timing | After paint | Before paint |
| Blocking | Non-blocking | Blocks visual updates |
| Use case | Most effects | DOM measurements |
| SSR | Safe | Warns on server |
SSR Considerations
useLayoutEffect doesn't run on the server, which can cause hydration mismatches. For SSR-safe code:
tsximport { useEffect, useLayoutEffect } from 'react'; // Use this instead of useLayoutEffect for SSR compatibility const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
Performance Warning
⚠️ useLayoutEffect blocks the browser from painting. Use it sparingly:
tsx// 🔴 Bad - expensive computation blocks paint useLayoutEffect(() => { const result = expensiveCalculation(); setData(result); }, []); // ✅ Good - only DOM measurements useLayoutEffect(() => { const rect = ref.current.getBoundingClientRect(); setDimensions(rect); }, []);