Provide a general summary of the issue here
Tree rendered inside Virtualizer from react-aria-components does not correctly virtualize when mounted inside a shadow root via ReactDOM.createRoot. The initial rendered range can be incorrect, and scrolling the tree container does not reliably update the rendered items.
This appears to be a regression in react-aria-components@1.17.0 / react-aria@3.48.0. The same reproduction works as expected in react-aria-components 1.16.x.
🤔 Expected Behavior?
A virtualized Tree inside a shadow root should behave the same as it does in the light DOM:
Initial render should start from the top of the tree.
Scrolling the tree container should update the virtualized item range.
Later items should render as the user scrolls.
Tree selection / expansion behavior should remain interactive.
😯 Current Behavior
When Tree is wrapped in Virtualizer and rendered inside a shadow root:
The rendered item range can be incorrect.
Scrolling the tree container does not reliably update the virtualized rows.
The scroll position changes, but the rendered items can remain stuck.
Non-virtualized tree behavior works, so the issue appears isolated to the virtualizer / scroll tracking path inside shadow DOM.
💁 Possible Solution
This was generated by LLM
root cause:
react-aria-components@1.17.0 introduced "Window scrolling in Virtualizer" and hardcoded allowsWindowScrolling: true in its private CollectionRoot component (the implementation behind the public Virtualizer export). This activates a fundamentally different visible-rect computation:
1.16.0 (working):
// Visible rect = just the scroll container's own position
new Rect(scrollLeft, scrollTop, clientWidth, clientHeight)
1.17.0 (broken):
// Visible rect = ancestor scroll offset + self scroll, clipped to window
new Rect(
viewportOffset.x + scrollPosition.x,
viewportOffset.y + scrollPosition.y,
Math.min(contentSize.width - viewportOffset.x, viewportSize.width),
Math.min(contentSize.height - viewportOffset.y, viewportSize.height) // ← clips items
)
viewportOffset is populated by a document.addEventListener('scroll', handler, true) ancestor-scroll listener. In Shadow DOM, this listener fires but nodeContains(target, ref.current) returns false (native Node.contains() can't cross shadow boundaries), so viewportOffset never updates — it stays at whatever getBoundingClientRect() returned at the initial mount position. The height formula then clips the visible rect to contentSize.height - mountY, shrinking the rendered window and leaving bottom items invisible.
The virtualizer appears to rely on scroll listeners / visible-rect calculation that are not correctly scoped to the shadow-root scroll container.
A possible fix would be to ensure virtualizer scroll handling works with shadow DOM by:
Listening directly on the virtualized scroll container, not only document.
Using shadow-DOM-aware event target handling.
Avoiding assumptions that scroll events or native event targets will behave the same after crossing a shadow boundary.
Ensuring visible rect calculation is based on the local scroll container when virtualizing inside a shadow root.
🔦 Context
We render components inside a shadow root for style isolation. After upgrading to react-aria-components@1.17.0, virtualized Tree behavior regressed inside shadow DOM.
This affects components that compose Tree + Virtualizer, including higher-level components that depend on virtualized tree/list rendering inside shadow-root portals or shadow-root app shells.
🖥️ Steps to Reproduce
https://codesandbox.io/p/devbox/funny-danny-nksfvn?workspaceId=ws_8Y7D6sLvDfpjduoiupTngc
Version
react-aria-components: 1.17.0 react-aria: 3.48.0
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
MacOS
🧢 Your Company/Team
athenahealth
🕷 Tracking Issue
No response
Provide a general summary of the issue here
Tree rendered inside Virtualizer from react-aria-components does not correctly virtualize when mounted inside a shadow root via ReactDOM.createRoot. The initial rendered range can be incorrect, and scrolling the tree container does not reliably update the rendered items.
This appears to be a regression in react-aria-components@1.17.0 / react-aria@3.48.0. The same reproduction works as expected in react-aria-components 1.16.x.
🤔 Expected Behavior?
A virtualized Tree inside a shadow root should behave the same as it does in the light DOM:
Initial render should start from the top of the tree.
Scrolling the tree container should update the virtualized item range.
Later items should render as the user scrolls.
Tree selection / expansion behavior should remain interactive.
😯 Current Behavior
When Tree is wrapped in Virtualizer and rendered inside a shadow root:
The rendered item range can be incorrect.
Scrolling the tree container does not reliably update the virtualized rows.
The scroll position changes, but the rendered items can remain stuck.
Non-virtualized tree behavior works, so the issue appears isolated to the virtualizer / scroll tracking path inside shadow DOM.
💁 Possible Solution
This was generated by LLM
root cause:
react-aria-components@1.17.0 introduced "Window scrolling in Virtualizer" and hardcoded allowsWindowScrolling: true in its private CollectionRoot component (the implementation behind the public Virtualizer export). This activates a fundamentally different visible-rect computation:
1.16.0 (working):
// Visible rect = just the scroll container's own position
new Rect(scrollLeft, scrollTop, clientWidth, clientHeight)
1.17.0 (broken):
// Visible rect = ancestor scroll offset + self scroll, clipped to window
new Rect(
viewportOffset.x + scrollPosition.x,
viewportOffset.y + scrollPosition.y,
Math.min(contentSize.width - viewportOffset.x, viewportSize.width),
Math.min(contentSize.height - viewportOffset.y, viewportSize.height) // ← clips items
)
viewportOffset is populated by a document.addEventListener('scroll', handler, true) ancestor-scroll listener. In Shadow DOM, this listener fires but nodeContains(target, ref.current) returns false (native Node.contains() can't cross shadow boundaries), so viewportOffset never updates — it stays at whatever getBoundingClientRect() returned at the initial mount position. The height formula then clips the visible rect to contentSize.height - mountY, shrinking the rendered window and leaving bottom items invisible.
The virtualizer appears to rely on scroll listeners / visible-rect calculation that are not correctly scoped to the shadow-root scroll container.
A possible fix would be to ensure virtualizer scroll handling works with shadow DOM by:
Listening directly on the virtualized scroll container, not only document.
Using shadow-DOM-aware event target handling.
Avoiding assumptions that scroll events or native event targets will behave the same after crossing a shadow boundary.
Ensuring visible rect calculation is based on the local scroll container when virtualizing inside a shadow root.
🔦 Context
We render components inside a shadow root for style isolation. After upgrading to react-aria-components@1.17.0, virtualized Tree behavior regressed inside shadow DOM.
This affects components that compose Tree + Virtualizer, including higher-level components that depend on virtualized tree/list rendering inside shadow-root portals or shadow-root app shells.
🖥️ Steps to Reproduce
https://codesandbox.io/p/devbox/funny-danny-nksfvn?workspaceId=ws_8Y7D6sLvDfpjduoiupTngc
Version
react-aria-components: 1.17.0 react-aria: 3.48.0
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
MacOS
🧢 Your Company/Team
athenahealth
🕷 Tracking Issue
No response