JavaScript Error
ResizeObserver loop completed with undelivered notifications.
Browser warning when ResizeObserver callbacks cause layout changes. Usually harmless but can indicate performance issues. Quick fixes: check for infinite resize loops, review callback logic, monitor frequency.
This cryptic error message appears when the browser’s ResizeObserver API detects potential infinite loops in resize callbacks. While the message sounds alarming, it’s often just the browser preventing performance problems by delaying notifications.
The error typically indicates that your resize callback is modifying element dimensions, which triggers more resize events. The browser breaks this cycle by deferring some notifications to the next frame.
Note: You won’t see this error in the browser console - it only appears in error monitoring tools because it’s dispatched as a window error event.
ResizeObserver loop completed is one of the most common JavaScript errors. It frequently ranks in the top 10 in the Global JavaScript Error Statistics.
The Problem
This error occurs when JavaScript uses the ResizeObserver API to watch for element size changes, and the callback function causes additional resize events during the same render cycle.
The browser automatically prevents infinite loops by deferring notifications, but reports this defensive action as an error:
// Example that triggers the error
const observer = new ResizeObserver((entries) => {
entries.forEach(entry => {
// This modification triggers another resize event
entry.target.style.width = '500px';
console.log('Resized to:', entry.contentRect.width);
});
});
observer.observe(document.getElementById('my-element'));
Key point: The “undelivered notifications” aren’t lost - they’re just delayed to prevent browser lockup. This is actually a safety feature working correctly.
Understanding the Root Cause
“ResizeObserver loop completed with undelivered notifications” can stem from different scenarios, ranging from harmless to problematic:
1. Normal Framework Behavior
Most common scenario: Modern frameworks and libraries legitimately trigger this error during normal operation. Chart libraries, responsive components, and layout systems often resize elements in response to container changes.
How to identify: The error occurs occasionally during page load or layout changes, then stops.
2. Third-Party Library Side Effects
Many popular libraries use ResizeObserver internally and may trigger this error:
- Chart.js and D3.js for responsive charts
- Material-UI and Ant Design for adaptive layouts
- Grid systems like AG-Grid or React-Table
- Image galleries and carousels
How to identify: The error correlates with specific components or library usage.
3. Custom Code Resize Loops
Your own ResizeObserver callbacks might unintentionally create resize cycles through direct style modifications or layout calculations.
How to identify: Errors occur consistently when interacting with specific elements or features you’ve built.
4. Performance-Impacting Infinite Loops
Problematic scenario: Callbacks that never settle into a stable state, continuously changing element sizes and consuming CPU resources. ResizeObserver issues can cause Long Animation Frames, or LoAFs.
How to identify: High frequency of errors, browser performance issues, or user reports of sluggish interface.
5. React/Vue Component Lifecycle Issues
Framework components might trigger resize loops during mounting, updating, or when props change, especially with responsive hooks or computed properties.
How to identify: Errors coincide with component state changes or prop updates.
How to Fix “ResizeObserver loop completed with undelivered notifications”
Quick Troubleshooting Checklist
- Check browser DevTools Performance tab for excessive layout thrashing
- Identify which components or libraries are using ResizeObserver
- Monitor error frequency - occasional vs continuous
- Test if errors affect actual functionality or user experience
- Look for recent changes to responsive components or layout code
- Check for direct style modifications inside resize callbacks
If the error impacts performance or occurs excessively, follow these debugging steps:
Step 1: Identify the Source
Use browser DevTools to pinpoint which code is causing the errors:
- Open DevTools Performance tab
- Start recording and reproduce the error
- Look for ResizeObserver entries in the timeline
- Check for layout thrashing indicators
// Add debugging to identify problem callbacks
const originalResizeObserver = window.ResizeObserver;
window.ResizeObserver = class extends originalResizeObserver {
constructor(callback) {
super((entries, observer) => {
console.log('ResizeObserver triggered:', entries.length, 'entries');
entries.forEach(entry => {
console.log('Element:', entry.target, 'Size:', entry.contentRect);
});
callback(entries, observer);
});
}
};
Step 2: Review Callback Logic
Examine your ResizeObserver callbacks for patterns that cause continuous resizing:
// Problematic: Direct style modification
const badObserver = new ResizeObserver((entries) => {
entries.forEach(entry => {
// This creates a resize loop
entry.target.style.width = entry.contentRect.width + 10 + 'px';
});
});
// Better: Use requestAnimationFrame to batch changes
const goodObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
entries.forEach(entry => {
// Batch DOM updates and add conditions to prevent loops
if (entry.contentRect.width < 500) {
entry.target.style.width = '500px';
}
});
});
});
Step 3: Implement Debouncing
For callbacks that perform expensive operations, add debouncing to reduce frequency:
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
const debouncedCallback = debounce((entries) => {
// Expensive resize handling here
entries.forEach(entry => {
updateChart(entry.target);
});
}, 100);
const observer = new ResizeObserver(debouncedCallback);
Step 4: Add Resize Guards
Prevent unnecessary callback execution with size change guards:
let lastWidth = 0;
let lastHeight = 0;
const guardedObserver = new ResizeObserver((entries) => {
entries.forEach(entry => {
const { width, height } = entry.contentRect;
// Only proceed if size actually changed significantly
if (Math.abs(width - lastWidth) > 5 || Math.abs(height - lastHeight) > 5) {
lastWidth = width;
lastHeight = height;
// Safe to perform resize operations
handleResize(entry.target, width, height);
}
});
});
Step 5: Handle Framework-Specific Cases
React components:
import { useEffect, useRef, useCallback } from 'react';
function ResponsiveComponent() {
const elementRef = useRef();
const observerRef = useRef();
const handleResize = useCallback((entries) => {
// Avoid state updates that trigger re-renders during resize
entries.forEach(entry => {
const { width, height } = entry.contentRect;
// Use refs instead of state for immediate DOM updates
if (elementRef.current) {
elementRef.current.dataset.width = width;
}
});
}, []);
useEffect(() => {
if (elementRef.current) {
observerRef.current = new ResizeObserver(handleResize);
observerRef.current.observe(elementRef.current);
}
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [handleResize]);
return <div ref={elementRef}>Responsive content</div>;
}
Vue components:
// Vue 3 Composition API
import { ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const element = ref(null);
let observer = null;
const handleResize = (entries) => {
// Avoid reactive updates that trigger watchers
entries.forEach(entry => {
const { width, height } = entry.contentRect;
// Use DOM manipulation instead of reactive data
element.value.style.setProperty('--dynamic-width', `${width}px`);
});
};
onMounted(() => {
if (element.value) {
observer = new ResizeObserver(handleResize);
observer.observe(element.value);
}
});
onUnmounted(() => {
if (observer) {
observer.disconnect();
}
});
return { element };
}
};
Step 6: Monitor Error Frequency
Track how often these errors occur to determine if they represent actual problems. Error monitoring tools like TrackJS can help you identify patterns, such as:
- Errors clustered around specific user actions
- High frequency from particular browser versions
- Correlation with performance complaints
- Spikes after code deployments
Monitoring helps distinguish between harmless framework noise and genuine performance issues requiring fixes.
When to Ignore This Error
“ResizeObserver loop completed with undelivered notifications” can often be safely ignored when:
- Occasional occurrence: A few errors during page load or layout changes
- Framework-generated: Third-party libraries working as designed
- No performance impact: Users don’t experience slowdowns or interface problems
- Stable after settling: Errors stop after initial layout calculations
However, investigate further if you notice:
- High frequency: Many errors per page load or user session
- Performance degradation: Sluggish interface or high CPU usage
- User complaints: Reports of slow or unresponsive interface
- Continuous errors: Errors that never stop occurring
Summary
The “ResizeObserver loop completed with undelivered notifications” error is usually a browser safety mechanism working correctly, not a critical bug. Most cases involve legitimate framework behavior or library usage that causes temporary resize loops.
Focus your attention on cases where errors occur frequently or impact performance. The key is implementing proper callback logic with guards, debouncing, and avoiding direct style modifications that create infinite resize cycles.
Remember: This error indicates the browser is successfully preventing infinite loops, which is actually good behavior protecting your users from browser lockup.