Measuring DOM Elements with useLayoutEffect
Read element dimensions, positions, and scroll offsets after layout but before paint to avoid flickering.
Measuring DOM Elements with useLayoutEffect is a free React Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
getBoundingClientRect Overview
element.getBoundingClientRect() returns a DOMRect object with the element's size and position relative to the viewport: top, right, bottom, left, width, and height. It forces the browser to perform a layout calculation to return accurate values, so call it sparingly.
The Problem with Reading in useEffect
If you read getBoundingClientRect() in a useEffect, the browser has already painted. If you then set state based on the measurement, React re-renders and the browser paints again. Users may see the element briefly in its wrong position or size before the correction appears in the second paint.
Why useLayoutEffect Solves This
Reading measurements in useLayoutEffect happens after DOM mutations but before paint. Any state update triggered by the measurement causes React to re-render synchronously and commit the corrected DOM — all before the browser has painted anything. The user sees only the final, correct result in a single paint cycle.
Tooltip Positioning
A tooltip needs to appear near its trigger element. Measure the trigger element's position in useLayoutEffect using getBoundingClientRect(), compute the tooltip's absolute position, and set that position in state. The tooltip renders in the correct location on the very first paint, with no visible jump from a wrong initial position.
Element Animation: Reading Initial Size
To animate an element from its natural (unconstrained) size to a fixed size, you need to know the natural size before applying the constraint. Read the element's height in useLayoutEffect before any animation style is applied, store it in state, and then use it as the starting keyframe value for your CSS transition.
Sticky Header Height
When a page has a sticky header, body content needs top padding equal to the header height to avoid being hidden behind it. Measure the header height in useLayoutEffect using a ref: headerRef.current.getBoundingClientRect().height. Set this as the paddingTop on the content container. The layout is correct from the first render.
State Update Causes Synchronous Re-render
When you call setState inside useLayoutEffect, React immediately runs another render and commit cycle in the same synchronous block. This re-render also runs its useLayoutEffect. Be careful not to create an infinite loop: only update state if the new measurement differs from the current state value.
SSR: Measurements Return Zero
On the server, DOM elements do not exist, so getBoundingClientRect() would throw or return zeros. Guard your useLayoutEffect with a check: if (typeof window === 'undefined') return;. For SSR apps, you may need to hydrate with a placeholder size and measure after hydration on the client.
ResizeObserver for Ongoing Tracking
One-time measurement with useLayoutEffect gives you the size at mount time. For tracking size changes as the element resizes (due to window resize, dynamic content, or CSS changes), use ResizeObserver instead. It fires a callback whenever the element's size changes, making it more suitable for responsive components.
Avoiding Redundant Measurements
If multiple effects need the same measurement, compute it once and share it via context or by passing it down as props. Computing the same getBoundingClientRect() value in multiple useLayoutEffect calls triggers multiple synchronous layout reads, each of which is relatively expensive. Consolidate measurements when possible.
Ref Timing with useLayoutEffect
Refs are populated before effects run. When useLayoutEffect fires, ref.current is guaranteed to point to the DOM element. This is why the pattern works: useLayoutEffect(() => { const rect = ref.current.getBoundingClientRect(); ... }, []). The ref is never null inside a layout effect on a mounted component.
getBoundingClientRect Timing
Why is it better to call getBoundingClientRect() in useLayoutEffect than in useEffect?
Lesson Recap: Measuring DOM Elements
Use getBoundingClientRect() in useLayoutEffect to read DOM measurements after mutations but before paint. State updates from these measurements cause a synchronous re-render included in the same paint, preventing flicker. Guard against SSR with a typeof window check. For ongoing size tracking, prefer ResizeObserver.
Frequently asked questions
Is the “Measuring DOM Elements with useLayoutEffect” lesson free?
Yes — the full text of “Measuring DOM Elements with useLayoutEffect” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Measuring DOM Elements with useLayoutEffect”?
Read element dimensions, positions, and scroll offsets after layout but before paint to avoid flickering. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Measuring DOM Elements with useLayoutEffect” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- The Rendering Phase and DOM Commit
- When useLayoutEffect Runs vs useEffect
- Measuring DOM Elements with useLayoutEffect
- Avoiding Layout Thrash and Visual Flicker