You're watching window to detect element size changes. ResizeObserver watches the element itself.
A `window` resize listener only fires when the viewport changes. ResizeObserver fires whenever the element's own box changes — from content loading, parent reflow, container queries, or anything else.

Here is something every frontend developer has written:
It works when the user drags the browser window. It does not work when the container changes size for any other reason: a parent flex layout shifts because sibling content loaded, a container query breakpoint triggers a CSS change, an image inside it loads and forces reflow, or you programmatically modify its children. window resize fires for one of the many things that can resize an element. ResizeObserver was built for the rest.
You pass a callback and tell it which elements to watch:
The callback fires whenever the observed element's size changes — regardless of why it changed. Stop watching with unobserve or disconnect:
The callback receives an array of entries because a single ResizeObserver can observe multiple elements, and the browser may batch several size changes into one notification. Always loop over all entries.
Each entry gives you three ways to read the new dimensions:
contentBoxSize and borderBoxSize use logical dimension names (inlineSize for width in LTR/RTL layouts, blockSize for height) so the same code works correctly in vertical writing modes. For standard horizontal layouts, contentBoxSize[0].inlineSize is the content width and borderBoxSize[0].inlineSize is what offsetWidth would return — without the layout read.
Runs right in your browser — poke at it and watch the concept react live.
The classic use case: keeping a <canvas> pixel-perfect as its container resizes.
With a window resize listener, you miss every canvas resize that happens without the viewport changing — panels collapsing, sidebars toggling, content loading. With ResizeObserver, the canvas always matches its container, at the right pixel density.
Unlike scroll listeners or window resize handlers, ResizeObserver does not fire on every animation frame. The browser batches and schedules callbacks after layout and before paint, at most once per frame. You do not need to debounce or throttle it:
The debounce would add latency without removing any work, because ResizeObserver already delivers at the right moment.
If your callback changes the observed element's size, it triggers another callback, which changes the size again. The browser detects this and logs ResizeObserver loop completed with undelivered notifications. The fix: do not write layout-affecting styles back to the element being observed inside the callback. If you need to respond to an element's width by applying a class or attribute (the element query pattern), make sure those class changes don't affect the element's own box dimensions — or defer the write with requestAnimationFrame so it lands outside the current resize notification cycle.
ResizeObserver is Baseline 2020: Chrome 64 (January 2018), Firefox 69 (September 2019), Safari 13.1 (March 2020). Every modern browser ships it. There is no equivalent in Node.js (no layout engine), but for anything running in a browser, no polyfill is needed.
Think it clicked? Take the 6-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
If your codebase has container.getBoundingClientRect() inside a window resize handler, it's solving the wrong problem with the wrong tool. window resize is a proxy for element resize that silently misses everything that doesn't involve the viewport. ResizeObserver watches the element. The callback fires when the element's box changes, and it hands you the new dimensions directly — no follow-up layout read required.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
- 💼 LinkedIn — linkedin.com/in/parsa-jiravand
- ✉️ Email (work & contract inquiries): bestpractice2026@gmail.com
Originally published on dev.to