react-component / react-component/resize-observer

Microtask deferral in SingleObserver causes ResizeObserver loop error — now reported by error trackers since Chrome 132

Open Beginner friendly
#236 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
201
Forks
47
PR merge metrics
No merged PRs in 30d

Description

Environment
Package Version
rc-resize-observer 1.4.3
React 19
Browsers Chrome/Edge 132+ (all Chromium-based)
Description

SingleObserver defers its onResize callback via a microtask at src/SingleObserver/index.tsx#L84-L87:

// defer the callback but not defer to next frame
Promise.resolve().then(function () {
    onResize(sizeInfo, target);
});

Microtasks execute within the same animation frame. When the deferred callback triggers a DOM layout change, the ResizeObserver fires again — creating a loop that exceeds the browser's depth limit. The browser then fires an ErrorEvent on window:

ResizeObserver loop completed with undelivered notifications.

Why this is now urgent — Chrome 132 change

This error has always occurred, but was previously invisible to error-tracking tools. Starting in Chrome 132, the ErrorEvent.error property changed from null to a string containing the error message (Chromium #391393420, chromium-dev thread).

Before Chrome 132 Chrome 132+
ErrorEvent.error null "ResizeObserver loop completed..."
Error trackers Silently discard Capture as unhandled error

Applications using rc-resize-observer (via antd Table, Typography, etc.) now see a large spike in error reports. Other projects have been affected by the same Chrome change: Odoo PR #196740.

Reproduction

CodeSandbox: https://codesandbox.io/p/sandbox/reproduction-rc-resize-observer-loop-error-t4crpg

import React, { useRef } from "react";
import RcResizeObserver from "rc-resize-observer";

const LoopTrigger = ({ index }: { index: number }) => {
  const ref = useRef<null | HTMLDivElement>(null);

  return (
    <RcResizeObserver
      onResize={() => {
        if (ref.current) {
          const current = ref.current.offsetHeight;
          ref.current.style.height = (current <= 20 ? 21 : 20) + "px";
        }
      }}
    >
      <div ref={ref}>Element {index}</div>
    </RcResizeObserver>
  );
};

export default function App() {
  return (
    <div>
      {Array.from({ length: 10 }, (_, i) => (
        <LoopTrigger key={i} index={i} />
      ))}
    </div>
  );
}

Each LoopTrigger wraps an element in <RcResizeObserver>. The onResize callback mutates the element's height, triggering another observation. Because rc-resize-observer delivers the callback via Promise.resolve().then(), the loop stays within the same frame. Open the codesandbox preview console — the error appears immediately on load.

Image
Root cause
  1. Browser layout phase detects size change
  2. Singleton ResizeObserver fires → observerUtil.js dispatches to listeners
  3. SingleObserver.onInternalResize defers via Promise.resolve().then()still same frame
  4. Microtask fires onResize → layout change → browser detects new size change → loop
  5. Exceeds depth limit → ErrorEvent with .error populated (Chrome 132+) → error trackers capture it
Suggested fix

Replace the microtask deferral with requestAnimationFrame to break the loop across frame boundaries:

      // Let collection know what happened
      onCollectionResize?.(sizeInfo, target, data);
      if (onResize) {
-       // defer the callback but not defer to next frame
-       Promise.resolve().then(function () {
-         onResize(sizeInfo, target);
-       });
+       // defer the callback to the next frame to break the ResizeObserver loop
+       requestAnimationFrame(function () {
+         onResize(sizeInfo, target);
+       });
      }

The comment in the existing code ("defer the callback but not defer to next frame") shows the microtask was intentional, but requestAnimationFrame would break the loop by deferring to the next frame — after the browser has finished processing all pending resize observations.

Related issues

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read src/SingleObserver/index.tsx#L84-L87 and reproduce the issue with the linked CodeSandbox, focusing on how callback scheduling affects repeated size changes. Done means the resize callback no longer keeps the loop within one frame and the reproduction no longer emits the reported ResizeObserver error.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.