useLayoutEffect Hook
In this page:
How useLayoutEffect Differs from useEffect
Both Hooks let you run code after rendering, but useEffect runs asynchronously after the browser has already painted, while useLayoutEffect runs synchronously before the paint happens. This means useLayoutEffect can prevent a visible flicker when it needs to adjust layout based on measurements.
Note: Default to useEffect for almost everything -- only switch to useLayoutEffect if you specifically see a visual flicker that needs fixing.
Warning: Because useLayoutEffect blocks painting until it finishes, slow code inside it can noticeably delay how quickly the page appears responsive.
Example: How useLayoutEffect Differs from useEffect
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function LayoutDemo() {
const boxRef = React.useRef(null);
React.useLayoutEffect(() => {
if (boxRef.current) {
console.log("Box width before paint:", boxRef.current.offsetWidth);
}
}, []);
return <div ref={boxRef} style={{width: "200px", padding: "10px", background: "lightblue"}}>Measured box</div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<LayoutDemo />);
</script>
</body>
</html>
A Typical Use Case: Avoiding Flicker
A common case for useLayoutEffect is measuring an element (like its height) and then immediately adjusting something else based on that measurement, such as positioning a tooltip. Doing this with useEffect could cause a brief, visible flash of the wrong position before it corrects itself.
Note: If your effect measures the DOM and then immediately updates styles based on that measurement, that's the classic useLayoutEffect scenario.
Warning: Most components never need this -- reach for it only when you've identified an actual visual flicker caused by a layout-dependent effect.
Example: A Typical Use Case: Avoiding Flicker
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Tooltip({ text }) {
const ref = React.useRef(null);
const [width, setWidth] = React.useState(0);
React.useLayoutEffect(() => {
setWidth(ref.current.offsetWidth);
}, [text]);
return <span ref={ref}>{text} (width: {width}px)</span>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<Tooltip text="Hover tip" />);
</script>
</body>
</html>
Server-Side Rendering Considerations
On a server (before any browser is involved), there's no real layout to measure, so useLayoutEffect produces a console warning there and doesn't actually run. In a purely browser-based setup (like the examples in this course), this isn't a concern.
Note: If you're building an app with server-side rendering (like Next.js), watch for this specific warning and consider whether useEffect would work just as well.
Warning: Seeing 'useLayoutEffect does nothing on the server' in a server-rendered app is expected -- it isn't a sign your code is broken.
Example: Server-Side Rendering Considerations
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function ClientOnlyMeasurement() {
const ref = React.useRef(null);
const [height, setHeight] = React.useState(null);
React.useLayoutEffect(() => {
setHeight(ref.current.offsetHeight);
}, []);
return <div ref={ref}><p>Measured height: {height ?? "measuring..."}</p></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<ClientOnlyMeasurement />);
</script>
</body>
</html>
- Reaching for useLayoutEffect by default instead of useEffect, even though useEffect is right for almost all cases.
- Not realizing useLayoutEffect runs synchronously and can block the browser from painting, hurting perceived performance if overused.
- Using useLayoutEffect on the server (in server-side rendering) where it produces a warning, since there's no browser layout to measure yet.
- useLayoutEffect has the same API as useEffect but fires at a different time.
- It runs synchronously after DOM mutations but before the browser paints the screen.
- It's mainly used to measure layout and make DOM adjustments before the user sees anything.
- useEffect is preferred by default; useLayoutEffect is reserved for layout-measurement edge cases.
Available since React 16.8, when Hooks were introduced.
Chapter Quiz — Complete all 12 topics to unlock
0/12 topics done
Complete these topics first: