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 (