useLayoutEffect
is a version of useEffect
that fires before the browser repaints the screen. useLayoutEffect
can hurt performance. Prefer useEffect
when possible.
References
useLayoutEffect(setup, dependencies?)
Call useLayoutEffect
to perform the layout measurements before the browser repaints the screen.
Parameters
setup
: The function with your Effect’s logic. Your setup function may also optionally return a cleanup function. Before your component is added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. Before your component is removed from the DOM, React will run your cleanup function.- optional
dependencies
: The list of all reactive values referenced inside of thesetup
code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is configured for React, it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like[dep1, dep2, dep3]
. React will compare each dependency with its previous value using theObject.is
comparison. If you omit this argument, your Effect will re-run after every re-render of the component.
Returns
useLayoutEffect
returns undefined
.
Caveats
useLayoutEffect
is a Hook, so you can only call it at the top level of your component or your own Hooks. You can’t call it inside loops or conditions. If you need that, extract a component and move the Effect there.- When Strict Mode is on, React will run one extra development-only setup+cleanup cycle before the first real setup. This is a stress-test that ensures that your cleanup logic “mirrors” your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, implement the cleanup function.
- If some of your dependencies are objects or functions defined inside the component, there is a risk that they will cause the Effect to re-run more often than needed. To fix this, remove unnecessary object and function dependencies. You can also extract state updates and non-reactive logic outside of your Effect.
- Effects only run on the client. They don’t run during server rendering.
- The code inside
useLayoutEffect
and all state updates scheduled from it block the browser from repainting the screen. When used excessively, this makes your app slow. When possible, preferuseEffect
. - If you trigger a state update inside
useLayoutEffect
, React will execute all remaining Effects immediately includinguseEffect
.
Use Cases for useLayoutEffect
Here are common and practical scenarios where useLayoutEffect
is the right tool:
1. Measuring DOM Elements (e.g. width, height, position)
If you need accurate layout measurements before rendering dependent content (like tooltips or animations), use useLayoutEffect
.
const boxRef = useRef(); useLayoutEffect(() => { const { width, height } = boxRef.current.getBoundingClientRect(); console.log('Box size:', width, height); }, []);
Guarantees you’re reading the actual DOM size before it’s visible to the user.
2. Synchronous DOM Manipulations
If you need to immediately update the DOM (e.g., scroll position, focus), and don’t want visible flickers:
const inputRef = useRef(); useLayoutEffect(() => { inputRef.current.focus(); }, []);
Unlike useEffect
, this avoids a “flash” where the input isn’t focused until after the paint.
3. Preventing Flicker in Animations
When triggering animations or layout changes, use useLayoutEffect
to apply styles before paint, so the transition is smooth.
useLayoutEffect(() => { elementRef.current.style.opacity = 1; // No flicker }, []);
If this were in useEffect
, users might see a brief state with opacity: 0
.
4. Reading & Writing Layout in Sequence
If you’re calculating layout and then updating it based on measurement:
useLayoutEffect(() => { const el = ref.current; const height = el.getBoundingClientRect().height; // Set a margin dynamically based on height el.style.marginTop = `${height}px`; }, []);
This ensures the DOM read/write happens before paint, avoiding layout thrashing or visual glitches.
5. Custom Scroll Handling
For sticky headers, auto-scrolling, or scroll-based effects, useLayoutEffect
ensures changes are in place before the browser paints.
useLayoutEffect(() => { window.scrollTo(0, 0); // Scroll to top immediately }, []);
When NOT to Use useLayoutEffect
- For side effects not related to layout or DOM, use
useEffect
instead. - Overuse of
useLayoutEffect
can block rendering and cause performance issues, especially on slower devices. - Avoid async operations inside it—it blocks paint until it’s done.
Key Difference from useEffect
:
- useEffect – Runs after paint
- useLayoutEffect – Runs before paint, after DOM updates