What is useLayoutEffect in React?
In this blog, let's learn about useLayoutEffect, its use cases, code examples, interview questions, and differences between useEffect vs useLayoutEffect
Kaushal Joshi
Dec 13, 2024 • 8 min read
When working with React, you might be familiar with useEffect
, the go-to hook for managing side effects. However, there's another hook that operates in a more synchronous manner: useLayoutEffect
. While less commonly used, useLayoutEffect
can improve user experience for scenarios requiring precise DOM manipulations.
In this blog, we'll dive deep into useLayoutEffect
, understand its behavior, compare it with useEffect
, and explore practical use cases. By the end, you'll know exactly when and how to use this hook to make your React applications more robust and efficient.
Prerequisites and Assumptions
This blog covers a hook that has a very niche use case. Hence, you might not use it on daily basis. This blog if for you, if...
Have a basic understanding of React, functional components, and hooks.
Be familiar with
useEffect
and its dependency array.Understand the rendering lifecycle of React components, including DOM updates and browser painting.
With that in mind, let's get started!
What Is useLayoutEffect Hook?
useLayoutEffect
is a React hook that allows you to run side effects synchronously after DOM mutations but before the browser paints the screen.
This behavior positions it between React's DOM updates and the browser's rendering phase. In simple terms, it provides an opportunity to modify the DOM or measure its properties before the user sees anything on the screen.
Painting the screen refers to the process where the browser fills in the visual details (like colors, text, and images) of the webpage after calculating the layout, making the page visible to the user.
Let's understand this in detail:
When React updates the DOM,
useLayoutEffect
executes immediately before the browser repaints. This ensures that any DOM measurements or manipulations are visible in their final state to the user.If you were to use
useEffect
in these cases, there could be a brief moment where the user sees a partially updated UI or an unintended flicker.With
useLayoutEffect
, as updates are done before the elements are visible to the user, there's no flickering or anything as such.
The useLayoutEffect
hook is like the stage crew setting everything up before the curtain rises, ensuring the stage is perfectly prepared. useEffect
hook on the other hand is like the actors reviewing the performance after the show, making adjustments for future performances.
Parameters of useLayoutEffect
The useLayoutEffect
hook shares its API with useEffect
, making it straightforward to use.
Callback Function:
The callback function contains the side effect logic. Just like the
useEffect
hook, it optionally returns a cleanup function. React executes this function after DOM updates but before painting the DOM on the browser.On re-renders, React first runs the cleanup function (if provided) for the old render. Then, it executes the callback with the new dependencies.
When a component unmounts, React runs the cleanup function for the last time.
Dependency Array:
Specifies dependencies for the callback. The callback runs only when these dependencies change. It could be a state, prop, or any variable or a function declared within the function.
An empty array (
[]
) ensures the effect runs only once after the initial render.
Your first useLayoutEffect hook:
Let's write a very simple snippet that demonstrates useLayoutEffect hook:
useLayoutEffect(() => {
console.log("This is useLayoutEffect speaking");
return () => console.log("Cleanup up what useLayoutEffect said...");
}, []);
useEffect(() => {
console.log("This is useEffect speaking...");
return () => console.log("Cleaning up what useEffect said...");
}, []);
Add this within your component and check the console. And you'd understand exactly when both hooks are triggered.
Lifecycle of useLayoutEffect
Because of its synchronous behavior, it's very important to understand the React lifecycle when useLayoutEffect
is used. Let's see the sequence from the start:
A component starts rendering.
React starts updating the DOM with updates values of the states and props.
The
useLayoutEffect
hook is executed, blocking the process of painting UI on the screen.Once the useLayoutEffect is finished executing, the browser starts painting UI on the screen. This is when the user sees something on the screen instead of while scren.
Once user sees the screen, the
useEffect
hook is triggered.
Here's a diagram that explains this flow:
When Should You Use useLayoutEffect
?
As useLayoutEffect blocks browser from painting UI on the screen, it affects performance negatively by increasing the time required for the user to see First Contentful Paint (FCP). Hence we must use this hook only when needed. Here are some scenarios where useLayoutEffect must be considered.
Measuring DOM elements
The most important use for useLayoutEffect
hook is to measure and update DOM dimensions. Suppose we are building a tooltip component. We must calculate the position of the tooltip based on the element which triggers the tooltip.
If we use the useEffect
hook to calculate the position, the element will flicker and change its position once its added to the screen. With useLayoutEffect
we can calculate the position beforehand, eliminating flickering completely.
Let's look at an example:
import React, { useState, useRef, useLayoutEffect } from "react";
const Tooltip = ({ text }) => {
const [isHovering, setIsHovering] = useState(false);
const [position, setPosition] = useState({ top: 0, left: 0 });
const buttonRef = useRef(null);
useLayoutEffect(() => {
if (isHovering && buttonRef.current) {
const buttonRect = buttonRef.current.getBoundingClientRect();
setPosition({
top: buttonRect.bottom + 8, // 8px below the button
left: buttonRect.left + buttonRect.width / 2, // Center horizontally
});
}
}, [isHovering]);
return (
<div>
<button
ref={buttonRef}
style={{ position: "relative" }}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
>
Hover me
</button>
{isHovering && (
<div
style={{
position: "absolute",
top: position.top,
left: position.left,
transform: "translateX(-50%)", // Center horizontally
}}
>
{text}
</div>
)}
</div>
);
};
export default Tooltip;
This is a tooltip component that takes text
as prop and renders it on the screen when the button is hovered. As it is a tooltip text, we must render it relative to the button element, so it displays accordingly.
We manage the button hovering state with
useState
hook.The tooltip's position is calculated using the
useLayoutEffect
hook. As a dependency, we passedisHovering
state.
When the isHovering
state updates and React re-renders the component, it will first calculate the position of the tooltip text based on the current position of the button element. The rendering process is paused until the calculation finishes.
When the calculation finishes, the browser starts painting on the screen. Now, if user hovers on the button component, the tooltip text will appear at correct position in the beginning itself.
Synchronous DOM updates
Another scenario where you'd find yourself using useLayoutEffect
is when you need to update the DOM before the user sees it. For example, animations. Let's see an example of animating a Box with GSAP. It's fine if you have never used GASP, you'd still understand what this snippet does:
import React, { useRef, useLayoutEffect } from "react";
import { gsap } from "gsap";
const AnimatedBox = () => {
const boxRef = useRef(null);
useLayoutEffect(() => {
const boxEl = boxRef.current;
if (boxEl) {
gsap.to(boxEl, { x: 300, duration: 1 });
}
}, []);
return (
<div ref={boxRef} style={{ width: 100, height: 100, background: "blue" }} />
);
};
export default AnimatedBox;
Here, useLayoutEffect
ensures the animation setup is complete before the user sees the box. Using useEffect
could result in a visible "jump" before the animation starts.
useEffect vs useLayoutEffect
The useLayoutEffect
hook is very similar to the useEffect
hook. However, there are some crucial differences between the two.
Behavior
The useLayoutEffect
hook behaves synchronously. That means, the code execution stops until the callback function is executed. Once the function executes, React moves on toward next things.
useEffect
on the other hand, is asynchronous. The code execution doesn't stop if useEffect occurs. Rather, the useEffect is triggered after other code is executed.
Timing
Continuing the last point, useLayoutEffect
runs before browser paints the DOM. This is because of its synchronous nature.
In contrast, useEffect
runs after browser has painted the screen.
Use Cases
The useLayoutEffect
is used for DOM measurements or synchronous updates. Whereas, useEffect
is used for non-UI-related tasks (e.g., data fetching).
When Not to Use useLayoutEffect?
You might be wondering, if useLayoutEffect
improves user experience, why not just use it every time instead of useEffect
hook? But no. You should not use this hook as a replacement for useEffect
hook.
Here are some scenarios where using useEffect
is better than useLayoutEffect
:
1. Data Fetching: Fetching data in useLayoutEffect
can block the UI thread, causing performance issues.
2. Server Rendering: useLayoutEffect
triggers warnings during SSR. Use useEffect
instead.
3. Unnecessary Use: Avoid using useLayoutEffect
unless absolutely necessary. Overuse can degrade performance.
Interview Questions
I am confident that this blog helped you completely grasp the useLayoutEffect
hook. Now, let's go through some questions an interviewer might ask you during frontend interviews.
When does
useLayoutEffect
run during the component lifecycle?What's the difference between
useEffect
anduseLayoutEffect
?When should you prefer
useLayoutEffect
overuseEffect
?How does
useLayoutEffect
affect performance?Why shouldn't you fetch data using
useLayoutEffect
?How to handle
useLayoutEffect
warnings on the server?
Coding Assignment
By now, you have a full understanding of what useLayoutEffect
is, how it works, when to use it, and most importantly, when not to use it. To finish off, write a component that implements what we learned today.
Create a React component that displays a box. Inside the box, render another smaller box centered within it. Use useLayoutEffect
to ensure the smaller box is perfectly centered after rendering.
Wrapping Up
I hope you found this blog helpful. If you manage to complete the coding assignment, I'd be very happy to know. I am most active on Twitter and Peerlist, if you want to say hi!
If you are looking for frontend developer jobs, or want to share a cool project you built, check out Peerlist!