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:

  1. Open DevTools Performance tab
  2. Start recording and reproduce the error
  3. Look for ResizeObserver entries in the timeline
  4. 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.

TrackJS is the easy way to monitor your JavaScript applications and fix production errors. TrackJS is provides detailed error monitoring and alerting to developers around the world at companies like 3M, Tidal, IKEA, Venmo, Allbirds, and Frontend Masters. TrackJS catches millions of errors from users everyday. Let's start catching yours.

Protect your JavaScript