useLayoutEffect Hook
In this page:
useLayoutEffect(() => {
// runs after DOM updates, before the browser paints
return () => {
// optional cleanup
};
}, [dependency]);
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.
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.
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.
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: