useLayoutEffect

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

useLayoutEffect

useLayoutEffect is a React Hook that is identical in signature to useEffect, but it fires synchronously before the browser repaints the screen.

Syntax

useLayoutEffect(setup, dependencies?)

useEffect vs useLayoutEffect

To understand why useLayoutEffect exists, you need to understand the timing of useEffect:

  1. React renders your component.
  2. The browser paints the screen (the user sees the UI).
  3. useEffect runs. (Asynchronous)

If your Effect mutates the DOM (e.g., measuring an element’s height and then immediately changing its style based on that measurement), the user will see a visible “flicker”. They will see the initial paint, and then a split-second later, they will see the DOM mutate again.

The timing of useLayoutEffect prevents this flicker:

  1. React renders your component.
  2. useLayoutEffect runs. (Synchronous)
  3. React waits for your useLayoutEffect to finish. If you update state inside it, React re-renders the component immediately.
  4. The browser paints the screen (the user sees the final UI).

Example: Tooltip Positioning

A classic use case is a Tooltip that needs to measure its own dimensions to decide whether to render above or below a button.

function Tooltip({ children, targetRect }) {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  // We use useLayoutEffect because we need to measure the DOM node
  // before the browser actually paints it to the screen.
  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, []); // Runs once right after the DOM is constructed, but before paint

  let tooltipY = 0;
  if (targetRect !== null) {
    tooltipY = targetRect.bottom;
    if (tooltipY + tooltipHeight > window.innerHeight) {
      // It doesn't fit below, put it above
      tooltipY = targetRect.top - tooltipHeight;
    }
  }

  return (
    <div ref={ref} style={{ top: tooltipY }}>
      {children}
    </div>
  );
}

Performance Warning

Because useLayoutEffect runs synchronously and blocks the browser from painting, it can hurt performance if overused. Your app will feel sluggish if you do expensive calculations inside it.

Rule of thumb: Always start with useEffect. Only switch to useLayoutEffect if you notice a visible UI flicker caused by your DOM mutations.

Interview Questions

Q: What is the core difference between useEffect and useLayoutEffect?
A: useEffect runs asynchronously after the browser has painted the DOM. useLayoutEffect runs synchronously before the browser paints the screen, literally blocking the paint.

Q: When should you reach for useLayoutEffect?
A: Almost never, unless you are measuring DOM nodes (like calculating a tooltip’s width) and need to immediately reposition an element before the user sees it, avoiding a visual flicker.