What is the difference between useEffect and useLayoutEffect?

👨‍💻 Frontend Developer 🟡 Often Asked 🎚️ Medium
#React #Hooks

Brief Answer

useEffect and useLayoutEffect are two hooks in React for working with side effects, but they execute at different times in the component lifecycle:

CharacteristicuseEffectuseLayoutEffect
Execution timeAsynchronously, after renderingSynchronously, before rendering
Blocks renderingNoYes
Visible to userMay cause flickeringNo flickering
PerformanceBetterWorse

When to use:

  • useEffect - for most side effects (API, subscriptions, timers)
  • useLayoutEffect - when you need to read or modify DOM before rendering (measurements, animations, synchronization)

Full Answer

The useEffect and useLayoutEffect hooks in React solve similar tasks but have key differences in execution time and performance impact.

What are useEffect and useLayoutEffect

Both hooks allow side effects in functional components but differ in execution timing:

  • useEffect - executes asynchronously after rendering the component to the DOM
  • useLayoutEffect - executes synchronously before rendering changes to the browser
import { useEffect, useLayoutEffect, useState } from 'react';
 
function Component() {
  const [count, setCount] = useState(0);
  
  // Executes asynchronously after rendering
  useEffect(() => {
    console.log('useEffect: Component rendered');
  });
  
  // Executes synchronously before rendering
  useLayoutEffect(() => {
    console.log('useLayoutEffect: Before rendering');
  });
  
  return <div>Counter: {count}</div>;
}

Key Differences

1. Execution Time

useEffect executes asynchronously after rendering:

function WithUseEffect() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    // Executes after rendering
    console.log('useEffect executed');
    document.title = `Counter: ${count}`;
  }, [count]);
  
  return <div>Counter: {count}</div>;
}

useLayoutEffect executes synchronously before rendering:

function WithUseLayoutEffect() {
  const [count, setCount] = useState(0);
  
  useLayoutEffect(() => {
    // Executes before rendering
    console.log('useLayoutEffect executed');
    document.title = `Counter: ${count}`;
  }, [count]);
  
  return <div>Counter: {count}</div>;
}

2. Blocking Rendering

useEffect does not block rendering:

function NonBlockingEffect() {
  const [width, setWidth] = useState(0);
  
  useEffect(() => {
    // Does not block rendering
    const calculateWidth = () => {
      // Heavy calculations
      let result = 0;
      for (let i = 0; i < 1000000; i++) {
        result += i;
      }
      setWidth(result % 1000);
    };
    
    calculateWidth();
  }, []);
  
  return <div>Width: {width}px</div>;
}

useLayoutEffect blocks rendering:

function BlockingEffect() {
  const [width, setWidth] = useState(0);
  
  useLayoutEffect(() => {
    // Blocks rendering until completion
    const calculateWidth = () => {
      // Heavy calculations
      let result = 0;
      for (let i = 0; i < 1000000; i++) {
        result += i;
      }
      setWidth(result % 1000);
    };
    
    calculateWidth();
  }, []);
  
  return <div>Width: {width}px</div>;
}

3. Visibility to User

useEffect may cause flickering:

function FlickeringComponent() {
  const [isVisible, setIsVisible] = useState(false);
  
  useEffect(() => {
    // May cause flickering
    if (isVisible) {
      document.body.style.backgroundColor = 'yellow';
    } else {
      document.body.style.backgroundColor = 'white';
    }
  }, [isVisible]);
  
  return (
    <div>
      <p>Background may flicker</p>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle background
      </button>
    </div>
  );
}

useLayoutEffect does not cause flickering:

function NonFlickeringComponent() {
  const [isVisible, setIsVisible] = useState(false);
  
  useLayoutEffect(() => {
    // Does not cause flickering
    if (isVisible) {
      document.body.style.backgroundColor = 'yellow';
    } else {
      document.body.style.backgroundColor = 'white';
    }
    
    // Cleanup
    return () => {
      document.body.style.backgroundColor = 'white';
    };
  }, [isVisible]);
  
  return (
    <div>
      <p>Background does not flicker</p>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle background
      </button>
    </div>
  );
}

When to Use Each Hook

Use useEffect for:

  1. Asynchronous operations - data loading, API calls
  2. Subscriptions - events, WebSockets
  3. Timers - setTimeout, setInterval
  4. Logging - tracking changes
// Loading data from API
useEffect(() => {
  async function fetchData() {
    const response = await fetch('/api/data');
    const data = await response.json();
    setData(data);
  }
  
  fetchData();
}, []);
 
// Event subscription
useEffect(() => {
  function handleResize() {
    setWindowSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
  }
  
  window.addEventListener('resize', handleResize);
  
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

Use useLayoutEffect for:

  1. Reading DOM - measuring elements
  2. Synchronization - precise positioning
  3. Animations - preventing flickering
  4. DOM mutations - changes before rendering
// Measuring element dimensions
useLayoutEffect(() => {
  const element = ref.current;
  if (element) {
    setDimensions({
      width: element.offsetWidth,
      height: element.offsetHeight
    });
  }
}, []);
 
// Scroll synchronization
useLayoutEffect(() => {
  const savedPosition = localStorage.getItem('scrollPosition');
  if (savedPosition) {
    window.scrollTo(0, parseInt(savedPosition));
  }
}, []);

Practical Examples

1. Measuring Elements

import { useLayoutEffect, useRef, useState } from 'react';
 
function ElementMeasurer() {
  const ref = useRef();
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
  
  useLayoutEffect(() => {
    if (ref.current) {
      const { offsetWidth, offsetHeight } = ref.current;
      setDimensions({
        width: offsetWidth,
        height: offsetHeight
      });
    }
  }, []);
  
  return (
    <div>
      <div ref={ref} style={{ 
        width: '200px', 
        height: '100px', 
        backgroundColor: 'lightblue' 
      }}>
        Measurable element
      </div>
      <p>Width: {dimensions.width}px</p>
      <p>Height: {dimensions.height}px</p>
    </div>
  );
}

2. Preventing Flickering

import { useLayoutEffect, useState } from 'react';
 
function FlickerPrevention() {
  const [count, setCount] = useState(0);
  const [color, setColor] = useState('red');
  
  useLayoutEffect(() => {
    // Change color before rendering, avoiding flickering
    setColor(count % 2 === 0 ? 'red' : 'blue');
  }, [count]);
  
  return (
    <div style={{ color }}>
      <p>Colored text: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
}

3. DOM Synchronization

import { useLayoutEffect, useRef } from 'react';
 
function DOMSynchronization() {
  const inputRef = useRef();
  
  useLayoutEffect(() => {
    // Focus element before rendering
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);
  
  return (
    <div>
      <input 
        ref={inputRef} 
        placeholder="This input will get focus without flickering"
      />
    </div>
  );
}

Summary

useEffect and useLayoutEffect are powerful tools for working with side effects in React:

When to use useEffect:

  • Asynchronous operations (API, subscriptions)
  • When flickering is not important
  • For most side effects

When to use useLayoutEffect:

  • Reading/modifying DOM before rendering
  • When you need to avoid flickering
  • For precise DOM synchronization

Key points:

  • useEffect executes asynchronously after rendering
  • useLayoutEffect executes synchronously before rendering
  • useLayoutEffect blocks rendering
  • useEffect is suitable for most cases
  • useLayoutEffect is used in specific scenarios

Understanding the difference between these hooks allows you to optimize performance and improve user experience in React applications.


Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site, and improve every day 💪