Know about the useInsertionEffect hook in React 18


React 18 provides a foundation for concurrent rendering. Several new APIs were introduced, which allow users to make full use of React’s concurrent rendering capabilities. In this blog, we will cover the new useInsertionEffect hook.

Problems faced in CSS-in-JS libraries

CSS libraries generate new rules on the fly and insert them with <style> tags into the document. For these libraries, it is necessary to know when to insert the <style> tags, as it may affect the performance.

When we add or remove any CSS rules, the browser more or less has to recalculate all the rules. It then has to reapply all the rules which already existed to all nodes, not just the changed ones.

In short, it causes a recalculation of all CSS rules against all DOM nodes in every frame while React is rendering. This is very slow.

The optimizations in browsers do not help to avoid this problem.

One way to avoid this is to manage the timing of updating DOM.

We must ensure to manipulate the CSS rules at the same time as other changes to the DOM. It can be when React mutates the DOM, before anything is read from the layout and before the content is visible in the browser.

This behavior is similar to the useLayoutEffect hook. Though it provides the desired behavior, we cannot use it to insert styles.

Why not use useLayoutEffect?

useLayoutEffect hook is used to read the layout from the DOM and synchronously re-render.

Assume we have some components that insert styles and some components that read layout. If we use the useLayoutEffect hook to insert styles, it causes the layout to be computed multiple times in a single pass. Additionally, if one hook tries to read the layout before the CSS has been inserted, it would read the incorrect layout.

So, to handle this use-case, a new hook was introduced - useInsertionEffect.

The useInsertionEffect hook

useInsertionEffect(didUpdate);

The signature of useInsertionEffect is identical to useEffect. But it fires synchronously before all DOM mutations.

It should be used for inserting global DOM nodes like <style> or SVG <defs> before reading layout in useLayoutEffect. It is not really meant to be used by anything else other than these CSS libraries.

This hook is limited in scope, so it does not have access to refs and cannot schedule updates.

//Source code - https://github.com/reactwg/react-18/discussions/110

function useCSS(rule) {
  useInsertionEffect(() => {
    if (!isInserted.has(rule)) {
      isInserted.add(rule);
      document.head.appendChild(getStyleForRule(rule));
    }
  });
  return rule;
}
function App() {
  let className = useCSS(rule);
  return <div className={className} />;
}

Note: useInsertionEffect effect does not fire on the server.

To know more about the hook check out the PR and more discussion here.

Join Our Newsletter