useEffect vs useLayoutEffect: Which One Should You Use?

useEffect runs after the browser paints, while useLayoutEffect runs after the DOM updates but before the paint. Learn which one fits each job.

6 min read

useEffect and useLayoutEffect share the same signature and cleanup rules, but they run at different moments. useEffect runs after the browser paints, while useLayoutEffect runs after the DOM updates but before the paint. Choose useEffect by default and reach for useLayoutEffect only when the UI would visibly flicker otherwise.

The same Hook, a different moment

Both Hooks take a setup function and a dependency array. The difference is when React calls the setup after a commit.

useEffectuseLayoutEffect
RunsAfter the browser paintsAfter DOM commit, before paint
Blocks paintingNoYes
Typical useData, timers, subscriptionsLayout measurements

Because useLayoutEffect blocks the paint, every state update inside it delays what the user sees. That is why the React docs recommend preferring useEffect whenever possible. The cleanup rules are identical, and both Hooks run one extra setup and cleanup cycle in development under Strict Mode.

When the paint timing matters

A tooltip needs its own height to decide whether it fits above or below the target. If you measure with useEffect, the first paint shows the wrong position and then it jumps.

App.jsxApp.jsx
import { useLayoutEffect, useRef, useState } from "react";
 
function Tooltip() {
  const ref = useRef(null);
  const [height, setHeight] = useState(0);
  useLayoutEffect(() => {
    setHeight(ref.current.getBoundingClientRect().height);
  }, []);
  return (
    <div ref={ref} className="tooltip">
      Hover text
    </div>
  );
}

The component renders once with an unknown height, then measures the DOM and re-renders, all before the browser paints. The user sees only the final, correctly positioned tooltip. With useEffect, the same measurement happens after the browser has already shown the tooltip in its initial spot, so fast users see a flicker.

When useEffect is the right default

Data fetching, timers, subscriptions, and analytics never need to block the paint. Anything that does not read layout information should stay in useEffect so the browser can paint first. useEffect lets the browser show the frame first, then the Effect runs.

App.jsxApp.jsx
useEffect(() => {
  const id = setInterval(() => setTicks((t) => t + 1), 1000);
  return () => clearInterval(id);
}, []);

There is no measurement here, so useLayoutEffect would only make the first frame slower with no visible benefit.

The server rendering caveat

useLayoutEffect has no layout information on the server, so React logs a warning during server rendering. The warning appears because there is no layout to measure before the HTML is generated. If a component needs it, keep that component client-only or render it only after hydration.

What to learn next

React useEffect explained covers the shared API, and how to synchronize React with browser APIs shows the subscription pattern both Hooks rely on. For reading DOM sizes safely, the refs section covers measuring elements on demand.

Rune AI

Rune AI

Key Insights

  • useEffect runs after the browser paints; useLayoutEffect runs before.
  • useLayoutEffect blocks painting, so it can make the page slower.
  • Use useLayoutEffect for layout measurements that would otherwise flicker.
  • useLayoutEffect does nothing on the server, so keep it in client-only components.
RunePowered by Rune AI

Frequently Asked Questions

When should I use useLayoutEffect?

When the component must measure the DOM and adjust what it renders before the browser paints, such as positioning a tooltip that would otherwise flicker.

Why not use useLayoutEffect everywhere?

It blocks the browser from painting, so too many layout Effects slow the page down. Use useEffect by default.

Does useLayoutEffect run on the server?

No. There is no layout on the server, so React warns that useLayoutEffect does nothing during server rendering.

Conclusion

useEffect and useLayoutEffect share a signature but run at different moments. Default to useEffect, and switch to useLayoutEffect only when a measurement must happen before the next paint.