--- title: useLayoutEffect --- `useLayoutEffect` can hurt performance. Prefer [`useEffect`](/reference/react/useEffect) when possible. `useLayoutEffect` is a version of [`useEffect`](/reference/react/useEffect) that fires before the browser repaints the screen. ```js useLayoutEffect(setup, dependencies?) ``` --- ## Reference {/*reference*/} ### `useLayoutEffect(setup, dependencies?)` {/*useinsertioneffect*/} Call `useLayoutEffect` to perform the layout measurements before the browser repaints the screen: ```js import { useState, useRef, useLayoutEffect } from 'react'; function Tooltip() { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); }, []); // ... ``` [See more examples below.](#usage) #### Parameters {/*parameters*/} * `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. Before your component is added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. Before your component is removed from the DOM, React will run your cleanup function. * **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. If you omit this argument, your Effect will re-run after every re-render of the component. #### Returns {/*returns*/} `useLayoutEffect` returns `undefined`. #### Caveats {/*caveats*/} * `useLayoutEffect` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a component and move the Effect there. * When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, [implement the cleanup function.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) * If some of your dependencies are objects or functions defined inside the component, there is a risk that they will **cause the Effect to re-run more often than needed.** To fix this, remove unnecessary [object](/reference/react/useEffect#removing-unnecessary-object-dependencies) and [function](/reference/react/useEffect#removing-unnecessary-function-dependencies) dependencies. You can also [extract state updates](/reference/react/useEffect#updating-state-based-on-previous-state-from-an-effect) and [non-reactive logic](/reference/react/useEffect#reading-the-latest-props-and-state-from-an-effect) outside of your Effect. * Effects **only run on the client.** They don't run during server rendering. * The code inside `useLayoutEffect` and all state updates scheduled from it **block the browser from repainting the screen.** When used excessively, this makes your app slow. When possible, prefer [`useEffect`.](/reference/react/useEffect) --- ## Usage {/*usage*/} ### Measuring layout before the browser repaints the screen {/*measuring-layout-before-the-browser-repaints-the-screen*/} Most components don't need to know their position and size on the screen to decide what to render. They only return some JSX. Then the browser calculates their *layout* (position and size) and repaints the screen. Sometimes, that's not enough. Imagine a tooltip that appears next to some element on hover. If there's enough space, the tooltip should appear above the element, but if it doesn't fit, it should appear below. In order to render the tooltip at the right final position, you need to know its height (i.e. whether it fits at the top). To do this, you need to render in two passes: 1. Render the tooltip anywhere (even with a wrong position). 2. Measure its height and decide where to place the tooltip. 3. Render the tooltip *again* in the correct place. **All of this needs to happen before the browser repaints the screen.** You don't want the user to see the tooltip moving. Call `useLayoutEffect` to perform the layout measurements before the browser repaints the screen: ```js {5-8} function Tooltip() { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); setTooltipHeight(height); // Re-render now that you know the real height }, []); // ...use tooltipHeight in the rendering logic below... } ``` Here's how this works step by step: 1. `Tooltip` renders with the initial `tooltipHeight = 0` (so the tooltip may be wrongly positioned). 2. React places it in the DOM and runs the code in `useLayoutEffect`. 3. Your `useLayoutEffect` [measures the height](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of the tooltip content and triggers an immediate re-render. 4. `Tooltip` renders again with the real `tooltipHeight` (so the tooltip is correctly positioned). 5. React updates it in the DOM, and the browser finally displays the tooltip. Hover over the buttons below and see how the tooltip adjusts its position depending on whether it fits: ```js import ButtonWithTooltip from './ButtonWithTooltip.js'; export default function App() { return (
This tooltip does not fit above the button.
This is why it's displayed below instead!
} > Hover over me (tooltip above)
This tooltip fits above the button
} > Hover over me (tooltip below)
This tooltip fits above the button
} > Hover over me (tooltip below) ); } ``` ```js ButtonWithTooltip.js import { useState, useRef } from 'react'; import Tooltip from './Tooltip.js'; export default function ButtonWithTooltip({ tooltipContent, ...rest }) { const [targetRect, setTargetRect] = useState(null); const buttonRef = useRef(null); return ( <>