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.
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.
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.
After learning a lot more about hooks and the lifecycle of SSR with those hooks, I think it is important to mention that useEffect and useLayoutEffect will always run after initial load from the server (I did not get this for some reason the first few times I was on this thread!). This means that whatever you choose to SSR render, will simply render as whatever you put in your
return (β¦)
first. Then, your useLayoutEffect will run and then your useEffect. But these happen after your component is already being shown to the user! The lifecycle of useLayoutEffect running before anything has rendered has changed in SSR.My main use of using useLayoutEffect was the affect height of elements based on window size before they were rendered by SSR, but that is not possibleβthey have to first render by SSR, which does not have access to the window! If you want something to change visually based on some browser/window state and variables before it is rendered, you cannot use useLayoutEffect or useEffect! The component will first render from server and then run these hooks, leading to size flickering, etc.
For me, it boils down to three options:
With SSR, both useLayoutEffect and useEffect run after your first render from the server, so I donβt really see any reason for someone needing to use useLayoutEffect.
For some reason, I just did not grasp this when I first was dealing with this, and came up with many convoluted workarounds. Hopefully this word dump will help someone.