How to get warning free usage of useLayoutEffect when server-side rendering

Alex Reardon
Apr 29 · 3 min read
Photo by Caleb George on Unsplash

useEffect and useLayoutEffect are React hooks that allow for the creation of side effects. They are a replacement for thecomponentDidMount, componentDidUpdate and componentWillUnmount lifecycle methods on class components.

Kent C. Dodds has put together a great guide on when to use useEffect and when to use useLayoutEffect. TLDR: most of the time you will want useEffect

useLayoutEffect is put forward as the safest upgrade path for class components

useLayoutEffectfires in the same phase as componentDidMount and componentDidUpdateReact Documentation

However, if you use useLayoutEffect then you will get a nasty SSR console warning

function App() {
useLayoutEffect(() => {
console.log("layout effect");
});
return "hello world";
}
// Will log a warning
const html = ReactDOMServer.renderToString(<App />);

⚠️ Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer’s output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://fb.me/react-uselayouteffect-ssr for common fixes.

Even though useLayoutEffect does nothing when using renderToString it will log a big old good time warning. Personally, I am not a fan of this behaviour given that useLayoutEffect is the safest upgrade path for class components. However, I can see the argument that for most people they should be using useEffect and this pushes them in that direction. But I still think logging a warning is too much.

A workaround (hack)

Create useIsomorphicLayoutEffect

// use-isomorphic-layout-effect.jsimport { useLayoutEffect, useEffect } from 'react';const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
export default useIsomorphicLayoutEffect;

Use instead of useLayoutEffect

// app.js
import useLayoutEffect from './use-isomorphic-layout-effect';
function App() {
useLayoutEffect(() => {
console.log('hello there');
}, []);
return 'Hello world';
};

No more SSR warnings! 😊

In this technique (hack), we conditionally return useLayoutEffect or useEffect based on whether we are running in the browser or not. Both useEffect and useLayoutEffect have the same API so your type system should be all good.

This hack is currently being used in react-redux and react-beautiful-dnd .

eslint

eslint-plugin-react-hooks is a really valuable eslint plugin if you are using hooks. In order for our custom use-isomorphic-layout-effect to correctly leverage the rules that the plugin has for useLayoutEffect, your import must be named useLayoutEffect . You can either do this by using a named export inuse-isomorphic-layout-effect

import { useLayoutEffect, useEffect } from 'react';const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
// Ensure the name used in components is useLayoutEffect
export default { useLayoutEffect: useIsomorphicLayoutEffect };

Or you can use eslint to force the name of the default export from use-isomorphic-layout-effect to be useLayoutEffect.

module.exports = {
// other config ...
rules: {
'no-restricted-imports': [
'error',
{
// Disabling using of useLayoutEffect from react
{
name: 'react',
importNames: ['useLayoutEffect'],
message:
'`useLayoutEffect` causes a warning in SSR. Use `useIsomorphicLayoutEffect`',
},
],
},
],
'no-restricted-syntax': [
'error',
// Ensure import from '*use-isomorphic-layout-effect' is `useLayoutEffect` to leverage `eslint-plugin-react-hooks`
{
selector:
'ImportDeclaration[source.value=/use-isomorphic-layout-effect/] > ImportDefaultSpecifier[local.name!="useLayoutEffect"]',
message:
'Must use `useLayoutEffect` as the name of the import from `*use-isomorphic-layout-effect` to leverage `eslint-plugin-react-hooks`',
},
],
}
}

Summary

Most of the time you will be using useEffect. useLayoutEffect logs a nasty warning in server-side rendering. You can hack around this warning by conditionally using useEffect in server environments. Boom.

Alex Reardon

Written by

JavaScript enthusiast

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade