useLayoutEffect
The useLayoutEffect is a React hook that runs synchronously after all DOM mutations, but before the browser paints the UI. It blocks the browser from painting anything until your code has run.
Syntax
useLayoutEffect(() => {
// DOM read or write
return () => {
// cleanup (optional)
};
}, [dependencies]);It is nearly identical to the useEffect hook, the only difference being that it runs syncronously before the browser paints, while useEffect runs asynchronously after the browser paints.
When to use
- To measure DOM elements, such as when getting the size of a
divusinggetBoundingClientRect(). - To update styles or scroll positions immediately, such as when using
scrollTo. - To prevent layout shift or flicker
When not to use
Do not substitute it with the useEffect hook. Therefore, do not use it for things such as:
- Fetching data from an API
- Creating timers
- Updating state unrelated to layout
Here are some examples:
Example 1: Measuring layout before paint
import { useRef, useState, useLayoutEffect } from 'react';
function Box() {
const boxRef = useRef(null);
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
if (boxRef.current) {
const rect = boxRef.current.getBoundingClientRect();
setWidth(rect.width);
}
}, []);
return (
<div>
<div
ref={boxRef}
style={{ width: '50%', background: 'lightblue', height: 100 }}
>
Resize me
</div>
<p>The box is {width}px wide</p>
</div>
);
}Example 2: Preventing layout shift
import { useLayoutEffect, useRef, useState } from 'react';
function ToggleBox() {
const [show, setShow] = useState(false);
const boxRef = useRef(null);
useLayoutEffect(() => {
if (!show && boxRef.current) {
// This is applied before the browser paints, and therefore prevents awkward visual jumps
boxRef.current.style.display = 'none';
}
}, [show]);
return (
<>
<button onClick={() => setShow(!show)}>
{show ? 'Hide' : 'Show'} Box
</button>
<div
ref={boxRef}
style={{
width: 200,
height: 100,
background: 'skyblue',
marginTop: 10,
}}
>
I am the box
</div>
</>
);
}Example 3: Scroll to bottom after DOM update
This example is a chat box that automatically scrolls to the latest message as soon as they are rendered:
import { useLayoutEffect, useRef } from 'react';
export function Chat({ messages }) {
const endRef = useRef(null);
useLayoutEffect(() => {
endRef.current?.scrollIntoView({ behavior: 'auto' });
}, [messages]);
return (
<div style={{ height: 200, overflowY: 'auto', border: '1px solid gray' }}>
{messages.map((msg, index) => (
<div key={index}>{msg}</div>
))}
<div ref={endRef} />
</div>
);
}Last updated on