ResizeObserver for React Developers

Solving ResizeObserver Errors

Chamara Senarath
5 min readDec 30, 2023

Recently, while working on a React project, I encountered the “ResizeObserver loop completed with undelivered notifications” error. Our team pinpointed that this error arises when the LastPass extension is activated in the browser. Although these errors won’t show up in the production environment and can be ignored safely, handling them during development becomes quite challenging.

Therefore, I delved into researching this to find a solution. Unfortunately, as of now, I haven’t come across a definitive solution to address this issue. In this blog post, I’ll share insights about ResizeObserver and discuss how I approached resolving the error.

What is ResizeObserver?

According to the MDN web docs,

The ResizeObserver interface reports changes to the dimensions of an Element's content or border box, or the bounding box of an SVGElement.

In simple terms, The ResizeObserver is like a watchman that keeps an eye on the size changes of HTML elements (like divs, images, etc.) or SVG elements.

Imagine you have a webpage, and you want to know when the size of a specific element changes, for example, when someone resizes the browser window or changes the content inside the element. You can use ResizeObserver to detect these changes and perform actions accordingly.

The basic usage of ResizeObserver involves creating an instance of the observer, specifying the target element(s) to observe, and defining a callback function that will be triggered when a size change occurs.

Here’s a simplified example of how ResizeObserver can be used:

// Create a new ResizeObserver
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
// Perform actions based on size changes
console.log('Element size changed:', entry.target);
console.log('New dimensions:', entry.contentRect.width, entry.contentRect.height);
// Perform actions like adjusting layout, styling, etc.
}
});

// Select the element to observe
const targetElement = document.querySelector('.resize-element');

// Start observing the target element
resizeObserver.observe(targetElement);

When the size of the observed element changes, the callback function provided to ResizeObserver will be executed, allowing us to handle the changes as needed.

Observation Errors

Observation errors in ResizeObserver generally refer to potential issues or mistakes that can occur when using this API. These errors can arise due to various reasons, including improper handling, incorrect usage, or unexpected behaviour of the observed elements.

Let’s say you have a webpage with multiple resizable elements. When you resize one element, it triggers a ‘resize event’. This event prompts the browser to check if other elements’ styles or layouts need to change. If these changes in turn cause more resize events, the browser handles these by ensuring it doesn’t endlessly loop through changes. Instead, it prioritizes changes deeper in the webpage structure to avoid infinite cycles.

If there’s a situation where an element’s resize affects another, which then triggers a change back to the first element (a loop), the browser safeguards against this by controlling the order of processing. If, due to these controls, some resize events are delayed until the next render, the browser reports this issue as an error with the message “ResizeObserver loop completed with undelivered notifications”, ensuring developers are aware of potential problems.

You can consider the following methods to minimize observation errors:
1. Implement proper error handling

2. Test your code across various scenarios

3. Implement techniques like debouncing or throttling to manage the frequency of resize events

Problem with third-party extensions

When third-party extensions are active in your browser and they modify elements on your webpage, observation errors may occur.

In our case, the LastPass extension adds its icon to input fields on the web pages, triggering ResizeObserver due to element resizing. If we attempt to load another component before ResizeObserver finishes handling all changes, it leads to an error.

For instance, this issue might arise in the login form. When trying to input login credentials and proceed to the next page by clicking “login,” an error might occur.

Another possible scenario is when inputs modified by extensions are within a modal with transitions, and the modals are forcibly closed before the transition completes.

We managed to resolve the modal-related error by allowing the modals to complete their transitions, providing ResizeObserver sufficient time to finish its actions.

However, in cases where adding a delay isn’t feasible, we needed an alternative solution. After attempting various workarounds, I found the following solution.

To address the issue, we’ll keep an eye on input elements altered by third-party extensions. Then, we’ll undo these alterations and disconnect the observer during the component’s unmounting phase. This will prevent ResizeObserver from continuing to monitor the element.

import { useRef } from 'react'

export default function Login() {
const inputRef = useRef(null)

useEffect(() => {
let resizeObserverEntries = []

const observer = new ResizeObserver((entries)=>{
resizeObserverEntries = entries
})

if(inputRef.current) observer.observe(inputRef.current)

return () => {
resizeObserverEntries.forEach((entry)=>entry.target.remove())
observer.disconnect()
}
},[])

return (
<>
<input name="username" type="text" />
</>
)
}

In the code above, We’ve used the useRef hook to create a reference for the input named “username”. Some extensions might wrap the input in another element like div and append their elements to it. You need to make any necessary adjustments in such cases. Also, you can create multiple references according to the number of input elements in your code.

Within the useEffect hook, We’ve set an empty array as the dependency array to invoke the callback function when the component mounts. Inside this callback function, an array named “resizeObserverEntries” is defined to track entries captured by the ResizeObserver.

Then, we create an instance of ResizeObserver and assign its entries to the resizeObserverEntries array. This instance of ResizeObserver monitors changes in our input element. We pass the reference of our input element to the ResizeObserver’s observe method. Therefore, the ResizeObserver starts monitoring changes in the input element, triggering the callback function when alterations occur. As a result, the resizeObserverEntries array updates when changes are detected in the input element.

Upon unmounting the current component, it’s essential to revert the changes and disconnect the observer. To achieve this, we use a return callback within the useEffect hook in React. Inside this callback, we remove all entries in the resizeObserverEntries array. However, if specifically targeting modified elements like the icon inserted by LastPass, you’ll need to identify that specific element in the DOM and handle it accordingly within this context. After handling these elements, disconnecting the observer ensures it stops monitoring the element’s changes.

And that’s a wrap for this blog! Hope you picked up something new about ResizeObserver and its error handling. Stay tuned for more blogs coming your way!

Follow me on Github, Twitter and LinkedIn for more valuable content. Looking forward to sharing more with you soon! 🙂

--

--