Most React devs know when to use useLayoutEffect. Almost none can explain why it behaves differently.
The answer lives inside the commit phase.
React's update cycle has two big stages: render (reconcile, diff, no DOM writes) and commit (apply changes, run effects). Commit itself splits into 3 sub-phases:
→ Before Mutation — read DOM snapshots before anything changes
→ Mutation — insert, update, remove DOM nodes
→ Layout — refs attached, useLayoutEffect fires here, synchronously
useEffect never runs in the Layout pass. During reconciliation, React builds an Effect List — fiber nodes tagged with pending work. Fibers marked HookLayout flush during the Layout sub-phase. Fibers marked HookPassive get handed to the scheduler and run after the browser paints.
React docs put it directly: "React guarantees that the code inside useLayoutEffect and any state updates scheduled inside it will be processed before the browser repaints the screen."
Classic case where this matters: tooltip positioning. With useEffect, users catch a flicker — the tooltip renders in the wrong spot, then jumps. With useLayoutEffect, both renders complete before any pixel changes on screen.
The tradeoff: useLayoutEffect blocks paint. Use it only when you need to measure or mutate the DOM before the user sees the frame. Data fetching, subscriptions, analytics — those belong in useEffect.
One gotcha: useLayoutEffect is a no-op in SSR. React will warn you. Guard with typeof window !== 'undefined' or default to useEffect in universal code.
#frontend #reactjs #javascript #typescript #frontenddevelopment #softwareengineering #webdevelopment
Lit 🔥