You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
With useScrollendEvent, scrollToIndex and scrollToEnd stop short when the target moves while they settle #1375
With useScrollendEvent: true, a scrollToIndex or scrollToEnd can stop before its target with no user input. It happens when the target moves while the command is settling. I found two ways:
In one ResizeObserver batch, a row above the viewport grows (the virtualizer corrects the scroll position for it) and a row in the viewport above the target grows too (no correction). The target moves further than the correction, and the command ends where the correction left the view.
It shows case 1 with the published @tanstack/react-virtual 3.14.26, pinned. All the code is in src/main.tsx, and it depends only on React.
Steps to reproduce
Open the page and click Run. It calls scrollToIndex(60, { align: 'center' }). In the frame after its scroll event, row 55 grows to 100px and row 58 to 150px.
Click run with false (useScrollendEvent: false), then click Run again.
Case 2: the index-shrink button on the command-retire e2e page, with useScrollendEvent: true on its virtualizer, ends at 4000 instead of 3900 in Chromium. WebKit fires no scrollend there.
Expected behavior
Both runs end with row 60 centred on the dashed line (scrollTop 2975).
Actual behavior
With useScrollendEvent: true the run ends at scrollTop 2875, 100px short, and the command has ended. With false it lands on 2975.
How often does this bug happen?
Every time
Platform
macOS 27.2, Chromium 156.0.8078.4 and WebKit 26.0 (Playwright builds); the same result in both.
Also seen in a chat app on Android WebView and iOS.
tanstack-virtual version
@tanstack/react-virtual 3.14.26 (@tanstack/virtual-core 3.19.11). Present since virtual-core 3.18.0 (scroll events retire commands, #1294) through main.
TypeScript version
5.9.3
Additional context
Each scroll write is reported a second time when the element comes to rest, and the virtualizer reads that second report as an external scroll, as if the user had scrolled away. With useScrollendEvent it is a scrollend at the same position in the same frame, so it arrives before reconcile has written the moved target. externalRetiresCommand sees the element neither where the command last wrote nor at the fresh target, and ends the command. With the debounced fallback, the second report comes after reconcile.
Activity