In React, useEffect and useLayoutEffect take exactly the same arguments. How do they differ in when React runs them, and what visible symptom tells you a piece of code needed useLayoutEffect?
answer
- it is about the paint boundary
- one runs before pixels, one after
- the wrong pick produces one bad frame
- synchronous work delays the frame
- default to the deferred one
basics
~20 suseLayoutEffect runs synchronously after React updates the DOM but before the browser paints; useEffect runs afterwards and never blocks the paint. Code that corrects the DOM before it is seen belongs in useLayoutEffect, otherwise the user sees a flicker.
solid answer
~40 sThey differ only in timing relative to the browser's paint. When React commits a render it mutates the DOM, then runs layout effects synchronously in that same task — the browser has not painted yet, so if the effect writes to the DOM or sets state, React can re-render and re-commit before a single frame reaches the screen. `useEffect` is deferred: React lets the browser paint the committed DOM and runs the effect after, so it can never delay the frame. The symptom of choosing wrong is a visible flash — an element rendered at the wrong scroll position or the wrong place for one frame, then jumping. The tradeoff is cost: everything inside `useLayoutEffect` is on the critical path to the pixel, so it stays the exception and `useEffect` stays the default.
code
jsx · 20 linesimport { useLayoutEffect, useRef } from 'react';
function MessageList({ messages }) {
const listRef = useRef(null);
// Runs after the DOM is updated but before the browser paints,
// so the list is never shown at the old scroll position.
useLayoutEffect(() => {
const el = listRef.current;
el.scrollTop = el.scrollHeight;
}, [messages]);
return (
<div ref={listRef} style={{ height: 200, overflowY: 'auto' }}>
{messages.map((m) => (
<p key={m.id}>{m.text}</p>
))}
</div>
);
}go deeper
Remember the one-line distinction: useLayoutEffect runs before the browser paints, useEffect runs after, and useEffect is the one you reach for by default. Be able to name flicker as the symptom of getting it wrong.
Explain the mechanism: React mutates the DOM, runs layout effects synchronously in the same task, then yields so the browser paints, and only then runs the deferred effects. Say why a state update inside a layout effect never shows an intermediate frame.
Show the cost side of the tradeoff. Layout effects sit on the critical path to the pixel and can force a second render-and-commit before the frame ships, so you justify each one and keep the work inside it minimal rather than reaching for it whenever timing feels flaky.
Own the standard: layout effects are an exception that reviews should question, because they are invisible in tests yet directly bound to frame latency. Be ready to say how you keep a codebase from accumulating them and how you would notice their cost in real user timing data.
## Same signature, different slot in the commit Both hooks take a setup function and an optional dependency array, both support a returned cleanup, and both follow identical re-run rules. Nothing about *what* they do differs. The only difference is *when* React calls them relative to the moment the browser turns the DOM into pixels. A React update ends with the DOM being mutated. Immediately after that mutation, still inside the same synchronous block of work and before the browser has had any opportunity to paint, React runs the layout effects — cleanups of the outgoing ones first, then the new setups. Only when that is finished does React return control to the browser, which paints. Effects registered with `useEffect` are deferred past that point: React lets the frame reach the screen and runs them afterwards. That single ordering difference is the whole answer, and every practical consequence follows from it. ## Why the difference is visible Consider a message list that should sit pinned to the bottom whenever new messages arrive: ```js useLayoutEffect(() => { const el = listRef.current; el.scrollTop = el.scrollHeight; }, [messages]); ``` React appends the new message nodes, then this effect scrolls the container — all before the browser paints. The user sees exactly one frame: the list already at the bottom. Swap it for `useEffect` and the browser paints the list *before* the scroll is applied. There is one frame showing the old scroll position with new content below the fold, then the correction lands and the content jumps. On a fast machine it reads as a flicker; on a slow one it is an obvious jump. Nothing is logically wrong, and no test that inspects the final DOM will catch it — the bug exists only in the frame the user actually saw. The same reasoning covers positioning something relative to another element and any correction that is derived from the just-committed DOM: if the uncorrected state would be visible and wrong, the correction must happen before paint. ## Why useEffect is still the default Everything inside a layout effect is synchronous work sitting between "DOM updated" and "pixels on screen". If it is slow, the frame is late and the interaction feels sluggish; if it triggers a state update, React must render and commit again before yielding, doubling the work on the critical path. Layout effects also cannot be deferred or interrupted the way React would like to schedule non-urgent work. So the rule of thumb is: use `useEffect` unless the code must run before the user can see the result. Sending analytics, opening a connection, starting a timer, syncing with an external system — none of that changes what is on screen this frame, so none of it should block the frame. ## The precise guarantee Be careful about how strongly you state the `useEffect` half. The guarantee you can lean on is that `useLayoutEffect` *always* runs before the paint of the commit that scheduled it. `useEffect` is deferred and is not permitted to hold the paint in the general case — React documents that when an update was not caused by a discrete interaction, the browser paints first. When the update did come from something like a click, React may flush the pending effect earlier. Treat `useEffect` as "after paint" for design purposes, but do not build logic that depends on a precise delay in either direction; if the timing genuinely matters, that is the signal that you wanted a layout effect. One more asymmetry worth knowing: neither hook runs during server rendering, but `useLayoutEffect` warns about it because a correction meant to happen before the first paint cannot happen at all in server-rendered HTML. ## How to answer the interview version Do not answer with "useLayoutEffect is synchronous and useEffect is asynchronous" and stop — that phrasing is true but tells the interviewer nothing about the paint boundary, which is the point. Lead with the boundary, name the symptom (a one-frame flash), name the cost (blocking the paint), and state the default. If you are asked for an example, reach for something that repositions or rescrolls a node that has just been committed, not for data fetching, which belongs in neither hook's critical path.
- If useLayoutEffect calls a state setter, does the user ever see the pre-update DOM?No. Because the layout effect runs before the browser paints, React processes that state update and re-renders and re-commits synchronously before yielding, so only the final result reaches the screen. That is exactly why the hook exists. The cost is that the frame now includes two render-and-commit passes, so keep the work small.
- Where do data fetching, analytics calls and timers belong — useEffect or useLayoutEffect?`useEffect`, without exception. None of them changes what is on screen for the current frame, so putting them in a layout effect only delays the paint for no visual benefit. The rule is mechanical: if the user could see a wrong intermediate frame without it, use `useLayoutEffect`; otherwise use `useEffect`.
- How would you actually confirm that a visual glitch is caused by using useEffect where a layout effect was needed?Reproduce it with CPU throttling so the extra frame is long enough to see, or record the interaction and step through frames — you will see one frame with the uncorrected layout followed by the corrected one. Switching that one effect to `useLayoutEffect` and seeing the intermediate frame disappear confirms the diagnosis.
saying these in an interview costs you the question
- useLayoutEffect is just the faster version of useEffect
- useEffect runs before render, useLayoutEffect after
- Use useLayoutEffect for data fetching so it starts sooner
- They are interchangeable, only the name differs
- useLayoutEffect blocks React rendering rather than the paint