What is useLayoutEffect Hook in React?

This article provides a comprehensive guide to the useLayoutEffect hook in React, explaining its purpose, how it functions, and how it differs from the standard useEffect hook. You will learn the specific scenarios where useLayoutEffect is necessary, such as measuring DOM elements to prevent visual flickering, along with a practical code example and performance considerations.

Understanding useLayoutEffect

The useLayoutEffect hook is a companion to useEffect in React. The primary difference between the two lies in the timing of their execution. While useEffect runs asynchronously after the browser has painted the updated screen, useLayoutEffect fires synchronously before the browser has a chance to paint those updates to the screen.

Because it runs synchronously, any state updates made inside useLayoutEffect will be processed before the browser redraws. This prevents the user from seeing a temporary, outdated visual state, making it ideal for manipulating or measuring the DOM.

The Difference: useEffect vs. useLayoutEffect

To understand when to use useLayoutEffect, it is helpful to look at the React rendering lifecycle pipeline:

  1. Render: React renders the components and calculates the Virtual DOM.
  2. Commit: React mutates the actual DOM.
  3. useLayoutEffect: React runs useLayoutEffect synchronously.
  4. Paint: The browser paints the changes onto the screen.
  5. useEffect: React runs useEffect asynchronously.

Because useEffect runs after the paint, if you modify the DOM or update state that affects the layout inside it, the user might see a brief flash or flicker. The element renders once in its initial state, and then immediately jumps to its new state. useLayoutEffect eliminates this flicker by running before the paint occurs.

When to Use useLayoutEffect

You should only use useLayoutEffect when your code directly interacts with the DOM and affects the visual layout of an element. Common use cases include:

For almost all other side effects—such as fetching data, setting up event listeners, or updating state that does not cause layout shifts—you should stick to useEffect to avoid blocking browser painting.

Code Example

Here is a practical example of using useLayoutEffect to measure the height of a DOM element before rendering content that depends on that measurement:

import React, { useState, useLayoutEffect, useRef } from 'react';

function Tooltip() {
  const [boxHeight, setBoxHeight] = useState(0);
  const elementRef = useRef(null);

  useLayoutEffect(() => {
    if (elementRef.current) {
      // Measure the actual height of the DOM element
      const height = elementRef.current.getBoundingClientRect().height;
      setBoxHeight(height);
    }
  }, []); // Empty dependency array runs once on mount

  return (
    <div>
      <div ref={elementRef} style={{ padding: '20px', background: 'lightgray' }}>
        Dynamic Content Area
      </div>
      <p>The height of the box above is: {boxHeight}px</p>
    </div>
  );
}

export default Tooltip;

In this example, React measures the element’s height and updates the state synchronously. The user sees the final, corrected layout immediately upon initial render, without any transitional jump or layout shift.

Performance Warning

Because useLayoutEffect is synchronous, it blocks the browser from painting the screen until the hook finishes executing. If you perform heavy computations or cause chain-reaction state updates inside this hook, it can significantly degrade your application’s performance and make the UI feel sluggish. Always default to useEffect unless you explicitly need to prevent visual glitching.