Skip to content

Instantly share code, notes, and snippets.

@gaearon
Last active April 15, 2024 10:06
Show Gist options
  • Save gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 to your computer and use it in GitHub Desktop.
Save gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 to your computer and use it in GitHub Desktop.
useLayoutEffect and server rendering

If you use server rendering, keep in mind that neither useLayoutEffect nor useEffect can run until the JavaScript is downloaded.

You might see a warning if you try to useLayoutEffect on the server. Here's two common ways to fix it.

Option 1: Convert to useEffect

If this effect isn't important for first render (i.e. if the UI still looks valid before it runs), then useEffect instead.

function MyComponent() {
  useEffect(() => {
    // ...
  });
}

Like useLayoutEffect, it won't run on the server, but it also won't warn.

Option 2: Lazily show component with useLayoutEffect

If UI looks broken with useEffect but gets fixed by useLayoutEffect, it means that this component doesn't look right until the effect runs. However, that means the server-rendered HTML version of it won't look right until JavaScript loads anyway. So server-rendering it brings no benefit and shows a confusing UI.

To fix this, you can delay showing that component until after the client side JS loads and hydrates the component. To exclude a Child that needs layout effects from the server-rendered HTML, you can render it conditionally:

function Parent() {
  const [showChild, setShowChild] = useState(false);
  
  // Wait until after client-side hydration to show
  useEffect(() => {
    setShowChild(true);
  }, []);
  
  if (!showChild) {
    // You can show some kind of placeholder UI here
    return null;
  }

  return <Child {...props} />;
}

function Child(props) {
  useLayoutEffect(() => {
    // This is where your layout effect logic can be
  });
}

For example, this is handy for jQuery plugins which can be initialized later.


If you have some use case that isn't covered, please report a complete minimal code example here and we'll try to help.

@sairion
Copy link

sairion commented Mar 30, 2023

It should be noticed the warning is removed for the upcoming versions.
See PR from the last week [Remove layout effect warning on the server #26395]:
facebook/react#26395

@icobg123
Copy link

icobg123 commented Apr 4, 2023

Everyone is discussing this issue in context of testing. I am seeing this issue due to integrations with non-react library (e.g. leaflet). I'd like to use IconButton and Avatar from material UI as marker on the map. The only solution from leaflet perspective to use DivIcon but it requires to render React Elements to string

const html = new L.DivIcon({
    className: 'my-div-ico',
    html: ReactDOMServer.renderToString(
       <IconButton>
          <Avatar>My</Avatar>
       </IconButton>
    ),
    ...
  })

Are there any suggestions about eliminating the warning in this case? None of proposals discussed in this thread do not work.

Hi @fogfish , have you been able to solve this? I've the same problem.

Thanks

did you find a solution to this? I'm in running into the same issue

@pm0u
Copy link

pm0u commented Jul 27, 2023

So if we're on React 18.2.0 and still seeing this error what does that mean given that a "fix" was supposedly merged?

@Viktorherald
Copy link

So if we're on React 18.2.0 and still seeing this error what does that mean given that a "fix" was supposedly merged?

I also having the same issue as you

i notice 18.2 is released on 2022, and the code is merged on 2023
so it is just a matter of time if React is releasing a new version anytime soon... or not

@pm0u
Copy link

pm0u commented Aug 7, 2023

@Viktorherald thanks, hadn't noticed the dates. waiting patiently!

@andrecasal
Copy link

andrecasal commented Sep 14, 2023

A better solution is to use an isomorphic effect.

hooks.ts:

import { useEffect, useLayoutEffect } from 'react'

export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect

Component.tsx:

import { useIsomorphicLayoutEffect } from 'hooks.ts'

const Component = () => {
	// Server? useEffect(). Client? useLayoutEffect() 🎉
	useIsomorphicLayoutEffect(() => {})

	return <p>Cool component</p>
}

If you're going to be using a lot of these hooks, you can also just npm i usehooks-ts.

@AlexIsMaking
Copy link

A better solution is to use an isomorphic effect.

This works great, thanks for sharing!

@nwazuo
Copy link

nwazuo commented Apr 6, 2024

A better solution is to use an isomorphic effect.

hooks.ts:

import { useEffect, useLayoutEffect } from 'react'

export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect

Component.tsx:

import { useIsomorphicLayoutEffect } from 'hooks.ts'

const Component = () => {
	// Server? useEffect(). Client? useLayoutEffect() 🎉
	useIsomorphicLayoutEffect(() => {})

	return <p>Cool component</p>
}

If you're going to be using a lot of these hooks, you can also just npm i usehooks-ts.

I use this, works great!

@gmjacob
Copy link

gmjacob commented Apr 15, 2024

A better solution is to use an isomorphic effect.

hooks.ts:

import { useEffect, useLayoutEffect } from 'react'

export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect

Component.tsx:

import { useIsomorphicLayoutEffect } from 'hooks.ts'

const Component = () => {
	// Server? useEffect(). Client? useLayoutEffect() 🎉
	useIsomorphicLayoutEffect(() => {})

	return <p>Cool component</p>
}

If you're going to be using a lot of these hooks, you can also just npm i usehooks-ts.

Thank you!

This solved my problem as well 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment