This warning appears when React detects useLayoutEffect during server-side rendering. Since layout effects require a DOM to measure and manipulate, they cannot run on the server, leading to potential hydration mismatches between server and client renders.
This warning occurs when you use React's useLayoutEffect hook in a component that is being server-side rendered (SSR). The useLayoutEffect hook is specifically designed to run synchronously after all DOM mutations but before the browser paints the screen. This timing allows components to read layout information (like element dimensions, positions, or scroll offsets) and synchronously re-render if needed before the user sees anything. However, during server-side rendering, there is no actual DOM, no layout information, and no browser paint cycle. The server is generating static HTML strings, not manipulating live DOM nodes. When React encounters useLayoutEffect during server rendering, it cannot execute the effect because the underlying browser APIs (like getBoundingClientRect, offsetWidth, etc.) don't exist in the Node.js environment. React warns about this because it creates a fundamental mismatch: the server-rendered HTML won't include any changes that useLayoutEffect would make, but when the component hydrates on the client, useLayoutEffect will run and potentially change the UI. This can cause visual "jumps" or layout shifts as content repositions itself after hydration, or worse, hydration errors if the mismatch is too significant.
The most widely adopted solution is to create a custom hook that uses useLayoutEffect on the client and useEffect on the server. This pattern is used by many popular libraries like react-redux and react-beautiful-dnd:
// lib/hooks/useIsomorphicLayoutEffect.ts
import { useEffect, useLayoutEffect } from 'react';
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
export default useIsomorphicLayoutEffect;Then replace all useLayoutEffect calls with this custom hook:
import useIsomorphicLayoutEffect from '@/lib/hooks/useIsomorphicLayoutEffect';
function MyComponent() {
useIsomorphicLayoutEffect(() => {
// Your layout effect logic
const element = document.getElementById('my-element');
const height = element?.offsetHeight;
// ...
}, []);
}This eliminates the warning while maintaining synchronous layout reading on the client.
If your effect doesn't need to run before the browser paints (e.g., logging, data fetching, subscriptions), simply replace useLayoutEffect with useEffect:
// Before
import { useLayoutEffect } from 'react';
function MyComponent() {
useLayoutEffect(() => {
console.log('Component mounted');
}, []);
}
// After
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Component mounted');
}, []);
}useEffect runs asynchronously after the paint, preventing the server warning and avoiding blocking the initial render. Use this when:
- You're not reading layout information
- A brief flash of content is acceptable
- The effect doesn't need to block visual updates
For components that absolutely need layout effects, render them only after the client has hydrated:
import { useEffect, useState, useLayoutEffect } from 'react';
function ComponentWithLayoutEffect() {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
useLayoutEffect(() => {
if (!isClient) return;
// Safe to use layout effect now
const element = document.getElementById('my-element');
const rect = element?.getBoundingClientRect();
// ...
}, [isClient]);
if (!isClient) {
return <div>Loading...</div>; // Fallback for SSR
}
return <div>{/* Actual content with layout effects */}</div>;
}This ensures the server and client render the same initial HTML (the loading state), then switches to the real content only on the client.
In Next.js, use dynamic imports with ssr: false to prevent server-side rendering of components that use layout effects:
// app/page.tsx
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('@/components/ComponentWithLayoutEffect'),
{ ssr: false }
);
export default function Page() {
return (
<div>
<h1>My Page</h1>
<ClientOnlyComponent />
</div>
);
}In the component:
// components/ComponentWithLayoutEffect.tsx
'use client';
import { useLayoutEffect, useState } from 'react';
export default function ComponentWithLayoutEffect() {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useLayoutEffect(() => {
// No warning - this only runs on client
setDimensions({
width: window.innerWidth,
height: window.innerHeight
});
}, []);
return <div>Window: {dimensions.width}x{dimensions.height}</div>;
}The "use client" directive and ssr: false ensure the component only renders on the client.
Wrap components using layout effects in Suspense boundaries to defer their rendering until client hydration:
import { Suspense } from 'react';
function Page() {
return (
<div>
<h1>Content above suspense renders immediately</h1>
<Suspense fallback={<div>Loading interactive content...</div>}>
<ComponentWithLayoutEffect />
</Suspense>
</div>
);
}This allows the server to render a fallback while the component with layout effects only renders on the client after hydration. In React 18+, this pattern is particularly effective with streaming SSR.
Many libraries have fixed SSR compatibility in recent versions. Check and update:
# Check current versions
npm list react-redux framer-motion react-hook-form
# Update to latest versions
npm install react-redux@latest framer-motion@latest react-hook-form@latestCommon libraries and their SSR fixes:
- react-redux v7.1+: Added useIsomorphicLayoutEffect internally
- framer-motion v6+: Improved SSR support
- react-hook-form v7+: Better SSR compatibility
- recharts v2+: Fixed useLayoutEffect warnings
If a library still causes warnings, check its GitHub issues for SSR workarounds or consider opening an issue.
Why React specifically warns about useLayoutEffect: While both useEffect and useLayoutEffect cannot affect server-rendered output, React specifically warns about useLayoutEffect because it's designed to run synchronously before paint. Using it during SSR creates an unavoidable mismatch - the server has no DOM to measure, but the client will measure and potentially re-render before the user sees anything, causing layout shifts.
Performance considerations: useLayoutEffect blocks the browser from painting until the effect completes. On the server, this doesn't matter (no browser), but on the client, it can delay time-to-first-paint. Only use useLayoutEffect when you must prevent visual flashing (e.g., tooltip positioning, DOM measurements for animations). For most cases, useEffect is sufficient.
React 18 and Suspense: In React 18, Suspense handles client-only rendering more gracefully. You can mark components as client-only by placing them inside Suspense boundaries, and React will skip server rendering them automatically.
The isomorphic layout effect debate: There's ongoing discussion about whether useIsomorphicLayoutEffect is semantically correct. Some argue it's a pragmatic solution, while others suggest it can hide real bugs. The key insight: if your effect truly needs layout measurements, it probably shouldn't run on the server at all. Consider whether delaying rendering (Step 3) is more appropriate.
Next.js App Router specific: Next.js 13+ with App Router uses Server Components by default, which never run client hooks. Add "use client" at the top of files using useLayoutEffect, and Next.js will handle client-only rendering automatically. However, you still need to handle third-party libraries that might use useLayoutEffect internally.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite