React Effect Timing
React effects run as part of the post-render lifecycle rather than during the component's synchronous render calculation. `useEffect` is a passive effect whose execution is generally deferred until after React commits the update, while `useLayoutEffect` runs after DOM mutations but before the browser repaints.
React rendering is expected to be free of side effects: the render phase calculates what the UI should look like. After React commits the resulting DOM changes, it can run effects registered with `useEffect`. This separation lets React finish applying the UI before passive effects perform external synchronization or other side effects.
`useEffect` is not simply equivalent to "always after paint." React normally lets the browser paint before processing passive effects, but interaction-driven updates can be scheduled differently. Therefore, code that requires the DOM to be measured or changed before the user sees the next frame should not depend on `useEffect` timing.
`useLayoutEffect` runs after React has committed DOM mutations but before the browser repaints. This makes it appropriate for layout measurement and synchronous DOM adjustments that must happen before the frame becomes visible. Because it can delay painting, it should be used only when its stronger timing guarantee is necessary.
An effect's dependency array controls when React needs to re-run the effect; it does not directly specify whether the effect is before or after paint. React also runs an effect's cleanup before running the next setup when dependencies change, and when the component is removed from the tree.
Key points
- Render should remain free of side effects; effects belong to the commit-related lifecycle.
- `useEffect` is a passive effect and normally runs after the updated UI has been committed and can be painted.
- `useLayoutEffect` runs after DOM mutation but before browser repaint.
- Interaction-caused effects can have timing different from the usual post-paint case.
- Use layout effects for DOM measurement or pre-paint adjustments, not as a default replacement for `useEffect`.
Interview questions
When does `useEffect` run relative to rendering?
The component first renders to determine the desired UI, and React then commits the resulting DOM changes. `useEffect` runs after that commit as a passive effect. In the common case, React lets the browser paint before running it. However, interaction-driven updates can have different scheduling, so "always after paint" is too strong.
What is the difference between `useEffect` and `useLayoutEffect`?
Both are hooks for synchronizing a component with external systems or imperative work, but their timing differs. `useLayoutEffect` runs after React commits DOM changes and before the browser repaints, allowing code to measure or adjust layout before the frame is visible. `useEffect` is passive and is generally deferred so it does not unnecessarily block painting. Therefore, layout effects are useful for visual work that must happen before paint, while ordinary synchronization usually belongs in `useEffect`.
Does `useEffect` always execute after the browser paints?
No. React's documentation describes passive effects as generally processed after the browser has had an opportunity to paint, but effects caused by an interaction may be processed before paint. React can also allow an effect to run before paint when required by its scheduling behavior. Therefore, application code should not rely on a universal post-paint guarantee from `useEffect`. If pre-paint execution is required for correctness or visual consistency, `useLayoutEffect` expresses that requirement explicitly.
Why can `useLayoutEffect` hurt performance?
A layout effect runs before the browser can paint the updated frame. If it performs expensive JavaScript or causes additional layout work, it can delay the frame becoming visible. This can make interactions feel slow and can contribute to layout-related performance problems. For work that does not need pre-paint timing, `useEffect` is usually a better fit.
What happens to an effect when its dependencies change?
When a committed render produces dependency values that differ from the previous render, React needs to synchronize the effect again. If the previous effect returned a cleanup function, React runs that cleanup before running the new setup. This pattern is important for subscriptions, timers, event listeners, and other external resources. The dependency list determines when synchronization is repeated; it does not determine whether the effect is a layout or passive effect.