From 628e427231ca4bb7f586a69f99fe7809749a53e2 Mon Sep 17 00:00:00 2001 From: Erick <101681592+devnomic@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:37:08 +0700 Subject: [PATCH] fix(message-scroller): avoid ResizeObserver loop error by coalescing resize handling into rAF (#11085) Co-authored-by: shadcn --- .../message-scroller-resize-observer-loop.md | 5 ++++ .../react/src/message-scroller/components.tsx | 30 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 .changeset/message-scroller-resize-observer-loop.md diff --git a/.changeset/message-scroller-resize-observer-loop.md b/.changeset/message-scroller-resize-observer-loop.md new file mode 100644 index 000000000..b564a6b27 --- /dev/null +++ b/.changeset/message-scroller-resize-observer-loop.md @@ -0,0 +1,5 @@ +--- +"@shadcn/react": patch +--- + +Fix MessageScroller firing "ResizeObserver loop completed with undelivered notifications" during streamed content growth by coalescing ResizeObserver callbacks into requestAnimationFrame. diff --git a/packages/react/src/message-scroller/components.tsx b/packages/react/src/message-scroller/components.tsx index 4b7acc295..81fce53dd 100644 --- a/packages/react/src/message-scroller/components.tsx +++ b/packages/react/src/message-scroller/components.tsx @@ -183,11 +183,22 @@ function MessageScrollerViewport({ return } - const observer = new ResizeObserver(handleResize) + // Coalesce into rAF: handleResize mutates the spacer inside the observed + // content, and resizing an observed element during delivery fires + // "ResizeObserver loop completed with undelivered notifications". + let frame = 0 + + const observer = new ResizeObserver(() => { + window.cancelAnimationFrame(frame) + frame = window.requestAnimationFrame(handleResize) + }) observer.observe(viewport) - return () => observer.disconnect() + return () => { + window.cancelAnimationFrame(frame) + observer.disconnect() + } }, [handleResize, viewportRef]) return ( @@ -261,11 +272,22 @@ function MessageScrollerContent({ return } - const observer = new ResizeObserver(handleResize) + // Coalesce into rAF: handleResize mutates the spacer inside this observed + // element, and resizing an observed element during delivery fires + // "ResizeObserver loop completed with undelivered notifications". + let frame = 0 + + const observer = new ResizeObserver(() => { + window.cancelAnimationFrame(frame) + frame = window.requestAnimationFrame(handleResize) + }) observer.observe(content) - return () => observer.disconnect() + return () => { + window.cancelAnimationFrame(frame) + observer.disconnect() + } }, [handleResize]) return (