What is the difference between useEffect and useLayoutEffect?

useEffect
99% of the time you should use useEffect
. This runs after the component has been rendered and ensure that your callback does not block the browser painting.
However, if your effect is mutating the DOM (via a DOM node ref) and the DOM mutation will change the appearance of the DOM node between the time that it is rendered and your effect mutates it, then you don't want to use useEffect
. You'll want to use useLayoutEffect
. Otherwise the user could see a flicker when your DOM mutations take effect. This is pretty much the only time you want to avoid useEffect
and use useLayoutEffect
instead.
// useEffect - runs asynchronously after render
useEffect(() => {
// Code here runs after browser has painted
}, [dependencies]);
useLayoutEffect
This runs synchronously immediately after React has performed all DOM mutations. This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element) and then make DOM mutations or trigger a synchronous re-render by updating state.
// useLayoutEffect - runs synchronously before render
useLayoutEffect(() => {
// Code here runs before browser paints
}, [dependencies]);
Key Differences
-
Timing:
- useEffect runs asynchronously after React has rendered the component and the browser has painted
- useLayoutEffect runs synchronously immediately after React has made DOM mutations but before the browser paints
-
Use Cases:
- useEffect is ideal for most side effects (data fetching, subscriptions, DOM updates that don't affect layout)
- useLayoutEffect should be used when you need to measure and update DOM layout (e.g., measuring scroll position, element dimensions)
-
Performance Impact:
- useEffect is non-blocking and won't delay visual updates
- useLayoutEffect can block visual updates since it runs synchronously
Summary
- useLayoutEffect: If you need to mutate the DOM and/or do need to perform measurements
- useEffect: If you don't need to interact with the DOM at all or your DOM changes are unobservable (seriously, most of the time you should use this).