What is useInsertionEffect in React?
This article provides a comprehensive guide to React’s
useInsertionEffect hook, detailing its purpose, timing, and
specific use cases. You will discover why this React 18 hook was
introduced to solve performance issues in CSS-in-JS libraries, how it
differs from useEffect and useLayoutEffect,
and how to implement it correctly in your applications.
Understanding useInsertionEffect
useInsertionEffect is a specialized React hook
introduced in React 18. It has the same signature as
useEffect, but it fires synchronously before any
DOM mutations take place. This means it executes before React calculates
the layout of the page and before the browser paints the pixels to the
screen.
Because it runs so early in the rendering lifecycle, it provides a
safe window to insert global DOM nodes like <style>
tags or SVG definitions before the rest of the layout is calculated.
The Problem It Solves
Before React 18, CSS-in-JS libraries (such as styled-components or
Emotion) typically injected <style> tags into the
document during the rendering phase or inside
useLayoutEffect. This approach caused two major performance
bottlenecks:
- Layout Thrashing: If style rules are inserted or modified while React is running layout effects, the browser is forced to recalculate style rules and layouts repeatedly.
- Reflows: Modifying active styles during layout calculation degrades rendering performance, especially in large application trees.
useInsertionEffect solves this by guaranteeing that
styles are injected into the DOM before any layout effects run.
By the time React and the browser calculate the physical layout of the
elements, the required styles are already present, preventing costly
style recalculations.
Syntax and Usage
The syntax for useInsertionEffect is identical to
useEffect. It accepts a callback function and an optional
dependency array.
import { useInsertionEffect } from 'react';
function useDynamicTheme(themeColor) {
useInsertionEffect(() => {
// Create and insert a style tag into the document head
const styleTag = document.createElement('style');
styleTag.textContent = `
.dynamic-box {
background-color: ${themeColor};
}
`;
document.head.appendChild(styleTag);
// Cleanup: remove the style tag when the component unmounts or color changes
return () => {
document.head.removeChild(styleTag);
};
}, [themeColor]);
}Guidelines for Using useInsertionEffect
To maintain application performance and avoid bugs, follow these
rules when working with useInsertionEffect:
- Use it exclusively for CSS-in-JS libraries: The React team explicitly states that this hook is meant solely for library authors who need to inject styles dynamically.
- Do not use it for app-level side effects: If you
need to fetch data, trigger animations, or handle event listeners, use
useEffectoruseLayoutEffect. - Avoid accessing Refs: Refs are not yet attached or
updated when
useInsertionEffectruns, meaning you cannot access DOM nodes via refs inside this hook. - Avoid updating state: You cannot trigger state
updates from within
useInsertionEffect.