How to Update useInsertionEffect in React

This article provides a straightforward guide on how to work with and update the useInsertionEffect hook in React. You will learn its specific use case for injection of dynamic styles, how to trigger updates using the dependency array, and the best practices for managing style cleanups to prevent memory leaks and layout thrashing.

Understanding useInsertionEffect

The useInsertionEffect hook is a specialized version of useEffect introduced in React 18. It runs synchronously before any layout effects (useLayoutEffect) and before the browser paints the screen.

This hook is designed strictly for CSS-in-JS library authors to inject <style> tags into the document head dynamically. Because it runs before layout effects, it ensures that the browser has the correct styles computed before it calculates the layout, avoiding performance bottlenecks.

How to Update useInsertionEffect

To update the styles injected by useInsertionEffect, you utilize its dependency array and a cleanup function, similar to how you manage updates in useEffect. When a dependency changes, React runs the cleanup function to remove the old styles and then executes the effect again to insert the updated styles.

Here is the step-by-step implementation:

1. Define the Hook with Dependencies

Pass the variables that dictate your styles (such as a theme or color) into the dependency array at the end of the hook.

2. Implement a Cleanup Function

Always return a cleanup function inside useInsertionEffect to remove the injected <style> tag from the document head. This prevents duplicate style rules from piling up in the DOM when updates occur.

Code Example: Updating Dynamic Styles

import { useInsertionEffect, useState } from 'react';

// Custom hook to insert and update dynamic styles
function useDynamicTheme(themeColor) {
  useInsertionEffect(() => {
    // 1. Create the style element
    const styleTag = document.createElement('style');
    styleTag.textContent = `
      .dynamic-button {
        background-color: ${themeColor};
        color: white;
        padding: 10px 20px;
        border: none;
        border-radius: 4px;
      }
    `;

    // 2. Insert the style into the document head
    document.head.appendChild(styleTag);

    // 3. Cleanup: Remove the old style tag when themeColor changes
    return () => {
      document.head.removeChild(styleTag);
    };
  }, [themeColor]); // React tracks this dependency to trigger updates
}

export default function App() {
  const [color, setColor] = useState('blue');

  // The hook will update whenever the "color" state changes
  useDynamicTheme(color);

  return (
    <div>
      <button className="dynamic-button">Styled Button</button>
      <button onClick={() => setColor('red')}>Change to Red</button>
      <button onClick={() => setColor('green')}>Change to Green</button>
    </div>
  );
}

Important Rules and Limitations

When updating or using useInsertionEffect, keep the following constraints in mind: