Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

ResizeObserver: Components That Respond to Their Own Size

C
Carlo Fontanos
· 2 min read

A chart lives in a panel. The user drags the panel divider, the panel grows, the chart... stays tiny in the corner. Window resize never fired - the window didn't resize. This mismatch between "viewport changed" and "my element changed" is exactly what ResizeObserver closes.

const observer = new ResizeObserver((entries) => {
    for (const entry of entries) {
        const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
        redrawChart(entry.target, width, height);
    }
});

observer.observe(document.querySelector('.chart-panel'));

Any reason the element changes size - dragged splitters, sidebar toggles, content growth, flexbox reflow, and yes, window resizes too - the callback fires with fresh dimensions. No polling, no getBoundingClientRect in a rAF loop.

The canvas case (where it's basically mandatory)

Canvas has a fixed pixel buffer; CSS just stretches it blurrily. Every properly responsive canvas is a ResizeObserver plus a buffer resize:

new ResizeObserver(([entry]) => {
    const { inlineSize, blockSize } = entry.contentBoxSize[0];
    canvas.width  = Math.round(inlineSize * devicePixelRatio);
    canvas.height = Math.round(blockSize * devicePixelRatio);
    render();
}).observe(canvas.parentElement);

JS size classes - when CSS container queries aren't enough

Container queries handle the styling side natively now, so don't use ResizeObserver for pure CSS switches. Use it when behavior changes with size: virtualized list row counts, chart tick density, whether a toolbar collapses into a menu (which needs measurement of its children, not just breakpoints):

new ResizeObserver(([entry]) => {
    el.classList.toggle('compact', entry.contentBoxSize[0].inlineSize < 480);
}).observe(el);

The famous error message, decoded

"ResizeObserver loop completed with undelivered notifications" haunts error trackers everywhere. It means: your callback changed layout, which resized an observed element again in the same frame; the browser deferred that next round to avoid an infinite loop. It's usually benign - but it's telling you your handler mutates what it measures. Fixes: don't resize the observed element from its own callback, or wrap mutations in requestAnimationFrame to push them to the next frame. And filter that message from your error reporting; it's noise 99% of the time.

Notes: entries batch multiple elements per callback (loop them, as above); contentBoxSize is an array for spec reasons - index [0] is what you want; observe border-box instead by passing {box: 'border-box'}. Support has been universal since 2020. Between this, IntersectionObserver and MutationObserver, "watch the DOM do things" is a fully solved category.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading