effects
useLayoutEffect
Runs a side effect synchronously after DOM mutations but before the browser paints. Reach for it only when you need to measure the DOM and then mutate it in a way the user must never see mid-flight (e.g. tooltip positioning).
useLayoutEffect(setup, dependencies?) Parameters & Behavior
| Parameter | Purpose |
|---|---|
| setup | Synchronous effect body; may return a cleanup function |
| dependencies | Same semantics as useEffect — re-runs when any value changes |
| blocking paint | Delays paint until the effect returns — keep the work small |
| SSR behavior | Emits a warning on the server; does not run there — guard with a useEffect fallback if needed |
Examples
const ref = useRef(null);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setHeight(height);
}, []); Measure an element before paint so a parent can lay itself out
useLayoutEffect(() => {
tooltipRef.current.style.top = `${anchorRect.bottom + 8}px`;
}, [anchorRect]); Position a tooltip synchronously to avoid a visible flicker
useLayoutEffect(() => {
window.scrollTo(0, savedScrollY);
}, [route]); Restore scroll position before the user sees the new page frame
const isoLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; Isomorphic pattern — use useEffect on the server to silence the SSR warning
Gotcha
Blocks paint, so heavy work here freezes the UI — default to useEffect and only escalate when a visible flicker forces you to. Does not run on the server.
Related hooks
useEffect
Runs a side effect after the browser has painted the committed render. Reach for it for subscriptions, data fetching, DOM syncing with external systems, timers, and anything that must not block the visual update.
useInsertionEffect
Runs before any DOM mutations from the current render are applied, giving CSS-in-JS libraries a chance to inject <style> tags first. Reach for it only if you are authoring a styling library — application code should not use it.
useRef
Returns a mutable object whose .current property persists for the lifetime of the component without triggering re-renders on change. Reach for it to hold DOM nodes, timers, previous values, or any imperative handle that render output does not depend on.