Synchronous Layout Calculations with useLayoutEffect0%

Synchronous Layout Calculations with useLayoutEffect

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Synchronous Layout Calculations with useLayoutEffect

React's useEffect is asynchronous and non-blocking: it executes after the browser paints the updated DOM to the screen. While this preserves 60 FPS UI performance for 95% of side effects, it causes visible visual flicker when measuring DOM nodes (e.g., tooltips, popovers, dropdown positions) and updating state before painting. useLayoutEffect runs synchronously before the browser paints.


1. The Rendering Lifecycle: useEffect vs useLayoutEffect

Output
React Component Updates
│
▼
React reconciles Virtual DOM and mutates physical DOM nodes
│
├───> useLayoutEffect fires SYNCHRONOUSLY
│ (Can inspect layout, measure dimensions, mutate DOM/state)
│
▼
Browser paints pixels to screen (layout and paint)
│
├───> useEffect fires ASYNCHRONOUSLY
(Data fetching, subscriptions, analytics)

If you position a tooltip dynamically inside useEffect:

  1. 1
    The DOM mounts with tooltip at (0, 0).
  2. 2
    The browser paints: user briefly sees the tooltip flicker at the top-left corner of the screen for 1 frame.
  3. 3
    useEffect runs, measures the trigger button, and sets coordinates (250, 180).
  4. 4
    The browser re-paints: tooltip jumps to the correct position.

With useLayoutEffect, the coordinate calculation happens before step 2, preventing any visual jump.


2. Dynamic Tooltip Positioning Example

TSX
import React, { useState, useRef, useLayoutEffect } from "react";
 
interface Position {
top: number;
left: number;
}
 
export function FloatingTooltipButton() {
const [showTooltip, setShowTooltip] = useState(false);
const [coords, setCoords] = useState<Position>({ top: 0, left: 0 });
 
const buttonRef = useRef<HTMLButtonElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
 
// useLayoutEffect guarantees coordinates are calculated BEFORE browser paint
useLayoutEffect(() => {
if (!showTooltip || !buttonRef.current || !tooltipRef.current) return;
 
const buttonRect = buttonRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
 
// Position tooltip centered directly above the trigger button
const calculatedTop = buttonRect.top - tooltipRect.height - 8;
const calculatedLeft = buttonRect.left + (buttonRect.width / 2) - (tooltipRect.width / 2);
 
setCoords({
top: Math.max(8, calculatedTop),
left: Math.max(8, calculatedLeft),
});
}, [showTooltip]);
 
return (
<div className="p-20 bg-slate-900 min-h-screen text-slate-100 flex items-center justify-center">
<button
ref={buttonRef}
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
className="px-6 py-3 bg-cyan-600 hover:bg-cyan-500 rounded-lg font-semibold shadow-lg transition"
>
Hover For Tooltip
</button>
 
{showTooltip && (
<div
ref={tooltipRef}
style={{
position: "fixed",
top: `${coords.top}px`,
left: `${coords.left}px`,
}}
className="z-50 px-3 py-1.5 bg-slate-800 border border-slate-700 text-cyan-300 text-xs rounded shadow-2xl pointer-events-none"
>
Dynamic tooltip anchored seamlessly without flicker!
</div>
)}
</div>
);
}

3. Server-Side Rendering (SSR) Warning

Because useLayoutEffect requires direct access to DOM geometric layout engines (getBoundingClientRect, offsetHeight), calling it during Server-Side Rendering (SSR in Next.js or Node.js) generates a warning:

Important Warning
"Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format."

Isomorphic Layout Effect Solution

To silence this warning in universal codebases, conditionally alias useLayoutEffect to useEffect on the server:

TypeScript
import { useEffect, useLayoutEffect } from "react";
 
export const useIsomorphicLayoutEffect =
typeof window !== "undefined" ? useLayoutEffect : useEffect;

Interactive Quiz

Practice Quiz

Question 1
When does useLayoutEffect execute in the React component lifecycle?
Question 2
Why does dynamic positioning inside standard useEffect cause visual flicker?
Question 3
What is the risk of performing heavy computational loops inside useLayoutEffect?
Question 4
Why does useLayoutEffect trigger a console warning during Server-Side Rendering (SSR)?
Question 5
What is the purpose of the useIsomorphicLayoutEffect pattern?
Next Lesson

External Store Synchronization with useSyncExternalStore

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext