When to useLayoutEffect Instead of useEffect (example)

By Dave Ceddia updated

When do you use useLayoutEffect Instead of useEffect?

There are two React hooks, useEffect and useLayoutEffect, that appear to work pretty much the same.

The way you call them even looks the same.

useEffect(() => {
  // do side effects
  return () => /* cleanup */
}, [dependency, array]);

useLayoutEffect(() => {
  // do side effects
  return () => /* cleanup */
}, [dependency, array]);

But they’re not quite the same. Read on for what makes them different and when to use each. (tl;dr: most of the time you want plain old useEffect)

The Difference Between useEffect and useLayoutEffect

It’s all in the timing.

useEffect runs asynchronously and after a render is painted to the screen.

So that looks like:

  1. You cause a render somehow (change state, or the parent re-renders)
  2. React renders your component (calls it)
  3. The screen is visually updated
  4. THEN useEffect runs

useLayoutEffect, on the other hand, runs synchronously after a render but before the screen is updated. That goes:

  1. You cause a render somehow (change state, or the parent re-renders)
  2. React renders your component (calls it)
  3. useLayoutEffect runs, and React waits for it to finish.
  4. The screen is visually updated
Without the right mental model, useEffect is super confusing.
With the right mental model, you'll sidestep the infinite loops and dependency warnings before they happen.
Get great at useEffect this afternoon with Learn useEffect Over Lunch.

99% of the time, useEffect

Most of the time your effect will be synchronizing some bit of state or props with something that doesn’t need to happen IMMEDIATELY or that doesn’t affect the page visually.

Like if you’re fetching data, that’s not going to result in an immediate change.

Or if you’re setting up an event handler.

Or if you’re resetting some state when a modal dialog appears or disappears.

Most of the time, useEffect is the way to go.

When to useLayoutEffect

The right time to useLayoutEffect instead? You’ll know it when you see it. Literally ;)

If your component is flickering when state is updated – as in, it renders in a partially-ready state first and then immediately re-renders in its final state – that’s a good clue that it’s time to swap in useLayoutEffect.

This’ll be the case when your update is a 2-step (or multi-step) process. Do you want to “batch” a couple updates together before redrawing the screen? Try useLayoutEffect.

I think of useLayoutEffect as the way to squeeze in a little extra work before React updates the DOM. “Hey, you’re making some changes already – could you throw this one in there too? Awesome.”

Here’s a (contrived) example so you can see what I mean.

When you click the page*, the state changes immediately (value resets to 0), which re-renders the component, and then the effect runs – which sets the value to some random number, and re-renders again.

The result is that two renders happen in quick succession.

import React, {
  useState,
  useLayoutEffect
} from 'react';
import ReactDOM from 'react-dom';

const BlinkyRender = () => {
  const [value, setValue] = useState(0);

  useLayoutEffect(() => {
    if (value === 0) {
      setValue(10 + Math.random() * 200);
    }
  }, [value]);

  console.log('render', value);

  return (
    <div onClick={() => setValue(0)}>
      value: {value}
    </div>
  );
};

ReactDOM.render(
  <BlinkyRender />,
  document.querySelector('#root')
);

* In general, putting onClick handlers on divs is bad for accessibility (use buttons instead!), but this is a throwaway demo. Just wanted to mention it!

Try the useLayoutEffect version and then try the version with useEffect.

Notice how the version with useLayoutEffect only updates visually once even though the component rendered twice. The useEffect version, on the other hand, visually renders twice, so you see a flicker where the value is briefly 0.

Should I useEffect or useLayoutEffect?

Most of the time, useEffect is the right choice. If your code is causing flickering, switch to useLayoutEffect and see if that helps.

Because useLayoutEffect is synchronous a.k.a. blocking a.k.a. the app won’t visually update until your effect finishes running… it could cause performance issues like stuttering if you have slow code in your effect. Coupled with the fact that most effects don’t need the world to pause while they run, regular useEffect is almost always the one to use.