Explain the timing guarantees of useEffect and useLayoutEffect, why useLayoutEffect blocks repainting, and when layout work needs a pre-paint guarantee. Passive effects generally run after paint for non-interaction updates, but interaction-caused effects may run before paint.
Frontend interview practice question
What’s the difference between useEffect and useLayoutEffect? When does it matter?
Interview quick answer
Interview focus
This React interview question tests whether you can explain useEffect vs useLayoutEffect: what is the difference, connect it to production trade-offs, and handle common follow-up questions.
- useEffect vs useLayoutEffect: what is the difference explanation without falling back to memorized definitions
- Hooks and Effects reasoning, edge cases, and production failure modes
- How you would answer the most likely React interview follow-up
Use this React interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
Full interview answer
Short answer
Passive useEffect callbacks generally run after paint for non-interaction updates, but React may run an interaction-caused effect before paint. Unlike useLayoutEffect, useEffect does not provide a pre-paint guarantee. useLayoutEffect runs after DOM mutations and blocks repainting until its setup and synchronous state updates finish, which matters for layout measurement and visible corrections.
The render pipeline (mental model)<br>For a typical non-interaction update, a useful model is:
1) Render (compute the next tree)
2) Commit (apply DOM changes)
3) useLayoutEffect runs and completes before repaint
4) Browser may paint the screen
5) useEffect generally runs afterward
This is not an absolute useEffect timeline: React may run an effect caused by an interaction before paint. The stable distinction is that useEffect offers no pre-paint guarantee, while useLayoutEffect does.
Hook | When it runs | Does it block paint? |
|---|---|---|
useLayoutEffect | After DOM updates, before paint | ✅ Yes (blocks paint) |
useEffect | Generally after paint for non-interaction updates; may run before paint for interactions | No layout-style pre-paint guarantee |
Why this difference existsuseLayoutEffect exists for code that must synchronously read or modify layout before repaint. useEffect is the default for synchronization that does not require that guarantee, such as subscriptions, logging, timers, or network coordination. Its exact position relative to paint can vary with the cause of the update.
function Example() {
const ref = useRef(null);
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
// Measure and synchronously adjust layout
}, []);
return <div ref={ref}>Hello</div>;
}
What happens if you use useEffect for layout work?
Because useEffect provides no pre-paint guarantee, the browser may paint the initial layout before the effect measures and corrects it. A later state update can then cause another paint, producing a visible flicker or jump. Use useLayoutEffect only when that pre-paint correction is required.
function Tooltip({ text }) {
const ref = useRef(null);
const [top, setTop] = useState(0);
// ❌ This can cause visible jump
useEffect(() => {
const rect = ref.current.getBoundingClientRect();
setTop(rect.top - 10);
}, []);
return (
<div ref={ref} style={{ position: 'absolute', top }}>
{text}
</div>
);
}
Correct version:
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setTop(rect.top - 10);
}, []);
// ✅ User never sees the wrong position
When you should use useLayoutEffect
Scenario | Why | Examples |
|---|---|---|
Measuring DOM size/position | Must read layout before paint | getBoundingClientRect, offsetWidth |
Synchronously adjusting layout | Prevent visible jumps | Tooltips, popovers, modals |
Scroll position corrections | Avoid one-frame wrong scroll | Scroll restoration, anchored lists |
Imperative animations setup | Need correct initial layout | FLIP animations, measurement-based motion |
When you should use useEffect (almost always)
• Data fetching
• Subscriptions / event listeners
• Logging / analytics
• Timers
• Syncing with external systems
These usually do not require a pre-paint layout guarantee. Their scheduling relative to paint can still vary, especially when an interaction causes the update.
Why you should avoid overusing useLayoutEffect
Because it blocks the browser from painting. Too many or heavy useLayoutEffect calls = slow first paint and janky UI.
SSR warninguseLayoutEffect does nothing on the server and causes warnings in SSR environments. Many frameworks alias it to useEffect on the server. Another reason to only use it when truly necessary.
Rule of thumb
Start with useEffect. Only switch to useLayoutEffect if you see a visual flicker, layout jump, or need to measure/mutate layout before paint.
Interview framing
Say it like this:
"Passive effects generally run after paint for non-interaction updates, but React may run an interaction-caused effect before paint. Unlike useLayoutEffect, useEffect does not provide a pre-paint guarantee. useLayoutEffect blocks repainting, so reserve it for layout measurement or corrections that must be invisible to the user."
Summary
useLayoutEffect runs after DOM mutations and blocks repainting, making it suitable for layout work that needs a pre-paint guarantee. useEffect is preferred for most external synchronization, but it is not accurate to promise that it always runs after paint. Overusing useLayoutEffect hurts performance, so use it only when visual correctness requires that guarantee.
Use this as one explanation rep, then continue with the React interview questions cluster or a guided prep path.