React useInsertionEffect — CSS-in-JS Style Injection

Understand useInsertionEffect for CSS-in-JS library authors. Covers when it runs, why it exists, comparison with useLayoutEffect, and practical examples of style tag injection.

Effect Hooks

Detailed Explanation

Style Injection with useInsertionEffect

useInsertionEffect is a specialized hook designed for CSS-in-JS library authors. It fires before DOM mutations and before useLayoutEffect, ensuring styles are available when layout effects read the DOM.

Execution Order

useInsertionEffect -> DOM mutation -> useLayoutEffect -> Paint -> useEffect

Why It Exists

CSS-in-JS libraries like styled-components, Emotion, and Stylex need to inject <style> tags into the document. Before useInsertionEffect, they used useLayoutEffect, which ran after DOM mutations. This meant there was a brief window where DOM nodes existed without their styles, causing incorrect measurements in other useLayoutEffect calls.

Example: Simple CSS Injection

let styleCache = new Map<string, HTMLStyleElement>();

function useCSS(rule: string) {
  useInsertionEffect(() => {
    if (styleCache.has(rule)) return;

    const style = document.createElement("style");
    style.textContent = rule;
    document.head.appendChild(style);
    styleCache.set(rule, style);

    return () => {
      document.head.removeChild(style);
      styleCache.delete(rule);
    };
  }, [rule]);
}

Restrictions

  • You cannot update state from inside useInsertionEffect
  • Refs are not attached yet when it runs
  • You cannot read or manipulate DOM nodes (they have not been created yet)
  • This hook is meant for library authors, not application code

Who Should Use This Hook

If you are building a CSS-in-JS library or a design system framework that dynamically injects styles, useInsertionEffect is the correct timing hook. Application developers should never need to call this hook directly.

Use Case

CSS-in-JS library authors use useInsertionEffect to inject style tags before DOM mutations, ensuring styles are available when useLayoutEffect runs measurements. Application developers should not need this hook directly.

Try It — React Hooks Reference

Open full tool