What the error means
ResizeObserver reports size changes before the browser paints. If a callback changes the size of an observed element, the browser checks again in the same frame, but only for elements deeper in the tree. Changes it cannot deliver move to the next frame, and the browser reports this error.
The error is sent to window.onerror, not thrown from your code, so it has no useful stack. The console may not show it, but window.onerror and error monitors do. Chrome used the old text ResizeObserver loop limit exceeded; current browsers use the text from the specification.
| Where | Message |
|---|---|
| Current Chrome, Edge, Firefox, Safari | ResizeObserver loop completed with undelivered notifications. |
| Older Chrome and Edge | ResizeObserver loop limit exceeded |
Common causes
- The callback sets the width, height, padding, or font size of the element it observes.
- The callback changes content or classes, and that changes the size of the observed element.
- A library (charts, virtual lists, auto-growing text areas) measures and resizes in the same callback.
- Two observed elements change each other's size, so each callback starts the other.
Reproduce it
Change the observed element's size inside its own callback. Listen on window to see the error, because the console may not show it.
window.addEventListener("error", (event) => console.log(event.message)); const box = document.querySelector<HTMLElement>("#box")!;new ResizeObserver(([entry]) => { box.style.width = `${entry.contentRect.width + 1}px`; // resizes itself}).observe(box);// ResizeObserver loop completed with undelivered notifications.Fix it
Write in the next frame, and only on a real change
Move the layout change into requestAnimationFrame, and skip it when the size did not change. Without the check, the resize runs again every frame.
TypeScriptlet lastWidth = 0; const observer = new ResizeObserver(([entry]) => { const width = Math.round(entry.contentRect.width); if (width === lastWidth) return; lastWidth = width; requestAnimationFrame(() => chart.resize(width));});observer.observe(container);Observe the container, resize the child
Watch an element whose size does not depend on what you change, such as a wrapper with a fixed or flexible width, and write to its child.
TypeScriptnew ResizeObserver(([entry]) => { canvas.width = Math.round(entry.contentRect.width); // child only}).observe(wrapper); // the wrapper's size does not follow the canvasDrop the known-benign message in your error monitor
When the layout is correct and the message comes from a library you do not control, filter it before it is sent. Keep your own layout code free of the loop.
TypeScriptinit({ apiKey: import.meta.env.VITE_SQUASHER_API_KEY, projectId: import.meta.env.VITE_SQUASHER_PROJECT_ID, beforeSend: (event) => event.message?.startsWith("ResizeObserver loop") ? null : event,});
Catch it in production
The Squasher browser SDK sends this error to your project when it happens, so you do not wait for a user to report it.
- The browser SDK listens to the window error event, so this message is captured like any uncaught error.
- beforeSend runs before every error event leaves the page. Return null to drop an event, or return a changed event.
- The release on each event shows if a new component version started the loop.
import { init } from "@squasher-ai/browser"; init({ apiKey: import.meta.env.VITE_SQUASHER_API_KEY, projectId: import.meta.env.VITE_SQUASHER_PROJECT_ID, release: import.meta.env.VITE_GIT_SHA, beforeSend: (event) => event.message?.startsWith("ResizeObserver loop") ? null : event,});Error: ResizeObserver loop completed with undelivered notifications.
Error
- Events
- 4,810
- First seen
- 9d ago
- Last seen
- now
- Environment
- production
- Service
- dashboard-web
Example code and issue.


