ResizeObserver Loop Notification Error
Problem Statement
The "ResizeObserver loop completed with undelivered notifications" error happens during rapid layout changes before a browser can repaint its content. This occurs when components trigger quick successive resizing events, exceeding the browser's processing threshold. Symptoms include:
- Errors appearing only during development, not in production builds
- Occurring after React Router version updates
- Only affecting specific page elements (not entire pages)
- Triggered by browser extensions or CSS transitions
- Often linked to modal dismissals, UI frameworks, or resizable components
This non-breaking console error indicates layout thrashing in your application—where component size calculations recursively trigger more resizing requests before previous changes are fully processed.
Solutions and Workarounds
1. Fix Root Causes (Recommended)
Address infinite resize loops with these techniques:
a) Debounce ResizeObserver Events
Delay processing using a 100ms buffer to avoid rapid-fire updates:
import { debounce } from 'lodash';
useEffect(() => {
const handleResize = debounce((entries) => {
entries.forEach(entry => {
// Update layout here
});
}, 100);
const observer = new ResizeObserver(handleResize);
observer.observe(elementRef.current);
return () => observer.disconnect();
}, []);
b) Use requestAnimationFrame
Ensure processing happens during browser repaint cycles:
const observer = new ResizeObserver((entries) => {
window.requestAnimationFrame(() => {
// Handle resize logic here
});
});
c) CSS Layout Optimization
Prevent thrashing with these CSS tweaks:
/* Set fixed widths/heights when possible */
.resizing-element {
min-width: 200px;
min-height: 150px;
}
/* Add containment to isolate layout changes */
scroll-container {
contain: strict;
}
2. Handle Browser Extensions
Password managers and grammar tools can inject hidden elements. Test by:
- Disabling extensions (LastPass, Grammarly, etc.)
- Replicating behavior in Incognito mode
- Adding extension-specific rules:
input::-webkit-credentials-auto-fill-button {
display: none !important;
}
3. Adjust UI Framework Settings
- Material UI: Set
minHeight
for multiline fieldsjsx<TextField multiline sx={{ "& textarea": { minHeight: "100px" } }}/>
- Ant Design: Remove problematic scroll propsjsx
<Table scroll={{ x: "max-content" }} /> {/* Remove this */}
4. Disable Dev Overlay (Last Resort)
Silence the error in development without affecting production:
Webpack Config:
module.exports = {
devServer: {
client: {
overlay: {
runtimeErrors: (error) => !error.message.includes('ResizeObserver')
}
}
}
};
React Hook:
useEffect(() => {
const handleError = (e) => {
if (e.message.includes('ResizeObserver loop')) return true;
};
window.addEventListener('error', handleError);
return () => window.removeEventListener('error', handleError);
}, []);
Why This Happens
ResizeObservers execute before browsers paint, causing recursion if:
- Element A's resize triggers Element B's size change
- Element B's change affects Element A's size
- The cycle repeats faster than repaints can occur
Third-party tools like browser extensions compound the issue by injecting elements during DOM transitions.
Caution
Ignoring this error via display: none
tactics hides but doesn't resolve the core layout issue. Prioritize CSS adjustments or debouncing to prevent performance impacts.
Production Safety
This error typically doesn't appear in production builds. Focus development fixes on:
- Identifying thrashing components
- Testing without browser extensions
- Confirming UX isn’t impacted
Final Recommendations
- Disable browser extensions to test
- Isolate components triggering resizes
- Add min-sizes to unstable elements
- Implement resize handling optimization
- Verify production build output
This pattern usually signals UI framework conflicts or layout instability—not core application issues. Use targeted fixes rather than global suppression for maintainable solutions.