useLayoutEffect and the warning on SSR

May 29, 2021

programming
article cover
Image by Sam Moqadam

Warning

I met the following warning on using useLayoutEffect with Gatsby.

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

In short terms, it’s telling us to make sure that we use useLayoutEffect only on client-side.
But before we get to the bottom of this, let’s take a bit of time to know the difference between useEffect and useLayoutEffect.

useEffect & useLayoutEffect

According to React’s official document, useEffect is defined as follows.

The function passed to useEffect will run after the render is committed to the screen.

I think no need to explain this to most React developers.
So then what is useLayoutEffect exactly? The official document says,

The signature is identical to useEffect, but it fires synchronously after all DOM mutations.

This means useLayoutEffect waits for not only the rendering process but until all elements are actually mounted on DOM.

Basically useEffect and useLayoutEffect are not different in that both of them do nothing on SSR. But since useLayoutEffect concerns not virtual DOM but real DOM on the browser, we get the warning.

Solution

It’s not gonna cause any trouble on build since it’s just a warning. But we always wanna get rid of this kind of warnings, don’t we?

To do that, we need to tell SSG to use useEffect on SSR and use useLayoutEffect on the client-side.
With SSG like Gatsby and Next.js, we can know whether it’s run on the browser by checking window object.

typeof window !== 'undefined'

Using this one, we make a custom hook that returns useEffect on SSR and useLayoutEffect on the client-side.

import { useEffect, useLayoutEffect } from 'react'

export const useIsomorphicEffect = () => {
  return typeof window !== 'undefined' ? useLayoutEffect : useEffect
}

Then we get no warning anymore!

import React, { useEffect, useLayoutEffect } from 'react'
import { useIsomorphicEffect } from './useIsomorphicEffect'

const App = () => {
  const isomorphicEffect = useIsomorphicEffect()
  
  isomorphicEffect(() => {
    // do something you want
  }, [])
  
  return (
    <div>
      Hellow world
    </div>
  )
}

Profile picture

Photographer / Web engineer working in Berlin.
A Japanese living with a Slovak wife and two German cats.