ResizeObserver: Depth-Order Measurement and Loop Limit Guard
- Last updated
- Prerequisites:
- DOM layout and CSS box model
- Understanding of the event loop and microtasks
- resize-observer
- layout
- responsive
- observers
- loop-limit
- measurement
Read at your depth
The practical view
ResizeObserver reports when an element's content-box or border-box size changes. const ro = new ResizeObserver((entries) => { for (const e of entries) { console.log(e.contentRect.width); } }); ro.observe(el);. Unlike window resize events, it observes individual elements and fires after layout settles, with an array of ResizeObserverEntry (target + contentRect + borderBoxSize/contentBoxSize). Useful for responsive components, canvas sizing, and text-overflow clamping. Call ro.disconnect() when done. Entries are delivered once per layout pass, batched; the API is built to avoid infinite feedback when you resize elements inside the callback.
The same idea in other frameworks
ResizeObserver is the modern replacement for window-resize-driven layout in React components (resize-detector, react-resize-observer).Angular apps can wrap ResizeObserver in directives; @angular/cdk's Layout module historically used window resize, moving to IO/RO where possible.vueuse's useResizeObserver exposes it as a composable; element-level responsive logic fits Composition API nicely.Svelte actions make ResizeObserver attachment declarative: use:resize creates the observer on mount and cleans up on unmount.Legacy vs modern
window.resize + manual measurement vs ResizeObserver
Window resize events miss element-level changes and need manual measurement; ResizeObserver observes the element itself with batched delivery after layout.
window.addEventListener('resize', () => { const w = el.offsetWidth; render(w); }); // ❌ OLD: only fires on window resize, forces layout readconst ro = new ResizeObserver(entries => { render(entries[0].contentRect.width); });
ro.observe(el); // ✅ NEW: observes the element, batched, after layoutInterview gotchas
Context
This is the defining design question of ResizeObserver — interviewers want the loop-limit mechanism and the depth-order processing rule, not just 'don't resize in the callback'.
The mechanical answer
Resizing an element inside the callback triggers another layout pass and another delivery. To prevent infinite feedback, the spec defines a bounded resize processing loop: per rendering update, the engine runs at most two processing passes. If after the limit an observation still reports a size change, the engine does not keep looping; it queues an error event (a 'ResizeObserver loop limit exceeded' error delivered as a task) and stops further resizing for that cycle. Elements are processed in depth order (parents before children), so a parent resize that changes child sizes is delivered coherently. This deterministic guard is why the API is safe to use for self-resizing components (e.g. canvas fit-to-container).
Trap
The naive answer is 'just don't resize inside the callback'. Self-resizing is a legitimate pattern; the loop limit makes it bounded rather than infinite. Another trap: assuming the error is thrown synchronously — it is reported asynchronously as an error event, so it can be missed in tests. Candidates who explain the two-pass limit and the error event demonstrate understanding of the API's core contract.
Context
This tests precise knowledge of the entry structure and the ordering semantics that prevent cascading measurement bugs in real layouts.
The mechanical answer
contentRect is the content-box rectangle (x, y, width, height) in CSS pixels, but it is computed relative to the element's padding box origin and can be lossy for subpixel or non-rectangular layouts. contentBoxSize and borderBoxSize are arrays of ResizeObserverSize objects ({ inlineSize, blockSize }) that preserve device-pixel precision and respect writing modes. Depth-order processing means the engine broadcasts entries parents-first: a parent's size change is delivered before its children, so child observations already reflect the parent's updated size — preventing the classic 'child measured before parent resized' bug. Using borderBoxSize when you care about the full box (borders included) is the recommended precise read.
Trap
The naive answer is 'contentRect is always the right measurement'. It is deprecated-by-preference in the spec for subpixel precision and has confusing x/y origin semantics. Another trap: assuming entries arrive in observation order — they arrive in depth order per the spec, so relying on insertion order for layout logic breaks. Candidates who cite contentBoxSize/borderBoxSize and depth-order broadcasting show real mastery.