Packages/@tanstack/virtual-core
Releases of @tanstack/virtual-core
83 stable releases, newest first. Release notes are kept for the last 90 days; older ones link to where they were published.
3.19.5
15 October 2026Patch Changes
fix(virtual-core): keep an instant scroll command clamped at a max the sizer committed outside a row resize
Before an instant scroll command's write is reported, the browser can clamp it at a smaller scroll max that the sizer moves to without a row resize: an append or prepend,
measure(), or a resize the sizer had not committed when the command was written. The virtualizer now also records the max at each commit, so such an event is read as its own clamped write and the command lands. A clamped write reported withisScrollingfalse is now written again too.fix(virtual-core): keep an instant scroll command when the browser clamps its write before rows grow back
Rows measured right after an instant
scrollToEnd()(orscrollToIndex) can first shrink the sizer below the written position and then grow it again in the same task. The browser clamps the write to the smaller max, and the scroll event reports that position. It was read as the user scrolling away, so the command was cancelled and the list stopped short of the end (a chat opened withestimateSizelarger than its rows). The virtualizer now follows the scroll max through those measurements and reads such an event as its own clamped write, so the command retargets and lands at the end.fix(virtual-core): scroll commands and corrections that stalled or were lost
On iOS 27 and later a
scrollTopwrite no longer stops a fling but is overridden by it within a frame. AscrollToIndex,scrollToOffsetorscrollToEndissued during a fling now waits for the fling to end instead of being lost. The user taking over before then cancels it: a drag, a second flick, or a finger stopping the fling (iOS reports none of these with touch events while the fling runs). Corrections keep being held through the gesture as on every version.scrollToOffset/scrollBy: a row above the viewport growing after the write's read-back moved the element off the fixed target, and the command kept reconciling until the 5s safety valve. The target now moves with the correction.A prepend (
anchorTo: 'end') before the read-back of a scroll command was anchored against the stale offset and written over the command. It is now left to reconcile, like a resize in that window.A scroll event between the prepend's options change and the adapter's commit no longer loses the anchor (one blank frame when the element could not be written by commit time).
A command's write clamped by a sizer that shrank below it before the read-back no longer reads as a user scroll; the command reaches its fresh target.
3.19.4
13 October 2026Patch Changes
fix(virtual-core): with
useAnimationFrameWithResizeObserver, request frames from the scroll element's window instead of the global one, and cancel frames still queued on cleanup, so a list rendered into another window does not stall while the opener is minimized or occluded
3.19.3
13 October 2026Patch Changes
refactor(virtual-core): simplify the scroll-correction state: a scroll write is tracked until its read-back as one record, the iOS touch gesture as one record, the pending prepend anchor as a tagged record, and every scroll command starts through one entry point (no behaviour change, with two exceptions: a second
touchend/touchcancelinside the post-touch grace window now restarts the window instead of leaving the first timer to end it early, and after the scroll element is swapped or the virtualizer disabled, a write to the old element is no longer matched to the new element's first scroll report).scrollAdjustmentsbecomes a read-only getter with the same value (assigning to it is no longer supported).
3.19.2
12 October 2026Patch Changes
fix(virtual-core): treat iOS
touchcancelliketouchendso interrupted gestures cannot leave touch state stuck and keep scroll anchoring suppressedrefactor(virtual-core): track an in-flight scroll command's lifecycle as one phase instead of several flags (no behaviour change)
3.19.1
12 October 2026Patch Changes
fix(virtual-core): let a user scroll end a non-smooth
scrollToIndexbefore reconcile has seen it land, so a later measurement no longer pulls the user back to the old targetfix(virtual-core): leave resizes measured before a scroll command's write is read back to reconcile, so an upward
scrollToIndexno longer jumps back and stalls until the safety valve, and buildscrollByon a just-written offset
3.19.0
11 October 2026Minor Changes
fix(virtual-core): keep lanes aligned after changing
laneswithmeasureElementoverscannow counts whole rows whenlanes > 1, so every lane renders (and measures) the same number of extra items. This renders more items than before:lanes: 4, overscan: 2now adds 8 items on each side instead of 2.Rangegains an optionallanesfield thatdefaultRangeExtractorreads; a customrangeExtractorgets row overscan only if it delegates todefaultRangeExtractor.A ResizeObserver callback now measures all of its entries before notifying, so a synchronous re-render can no longer unmount part of a row before it is measured.
3.18.0
9 October 2026Minor Changes
feat(virtual-core): add
cancelScroll()to stop an in-flightscrollToIndex/scrollToOffset/scrollBy/scrollToEndfrom correcting toward its target, so a user gesture can take over the viewport
Patch Changes
Fix
scrollToIndex(last, { align: 'end' })overshooting the last item bypaddingEnd; it now respectsscrollPaddingEndlike other end-aligned items. To scroll to the very bottom includingpaddingEnd, usescrollToEnd().
3.17.11
14 September 2026Patch Changes
Read the current scroll offset when the debounced scroll-end fallback fires so measurement adjustments made since the last browser scroll event are not overwritten by stale state.
Keep a travelling smooth
scrollToIndexalive when content is prepended. WithanchorTo: 'end', the prepend anchor sync wrotescrollTopinstantly, which cancelled the browser's smooth animation and left the scroll stranded partway; Chromium drops a smooth request re-issued right after such a cancel, so it could not be resumed. The sync is now skipped while a smooth programmatic scroll is still in flight, and the animation continues to its recomputed target. A smooth scroll that has already landed still receives the anchor sync.
3.17.10
11 September 2026Patch Changes
Keep an end-pinned virtualizer following appended items when older items are trimmed in the same update and the item count does not increase. Recognize ordered, overlapping windows while preserving reading anchors for users who have scrolled away from the end.
Preserve item keys in the lazy measurement cache so a stable
getItemKeycallback reading mutable data cannot change the identity of previously measured rows.
3.17.9
7 September 2026Patch Changes
Recover the bottom pin when the browser clamps an end-anchored scroll compensation write.
resizeItemcompensates a size change by writingscrollTopbefore the consumer has committed the new total size, so when the grown item does not itself extend the scroll range the browser clamps the write to the old maximum and the viewport is left short of the end with no scroll event to correct it. Two cases hit this:paddingEnd > 0with a growing last item, where the overflowing item only extendsscrollHeightto its own end and the clamp lands exactlypaddingEndshort (#1258); and a row above the last one growing while the last row keeps its size, underdirectDomUpdates(#1266). A compensation write whose target exceeds the scroll maximum at write time is now recorded as clamped and re-issued once the sizer has grown — right afternotifyfor consumers that size the container synchronously inonChange, and from_willUpdatefor consumers that size it during a render. The clamped read-back keeps the retry pending; any other scroll event cancels it, so a user reading history is never yanked.
3.17.8
18 August 2026Patch Changes
Cancel the pending
isScrollingreset when a scroll observer is torn down, and resetisScrollingandscrollDirectionincleanup()so they don't stay stuck after the scroll element changes or is removed.Ignore connected measurement nodes whose indexes are outside the current item count.
3.17.7
28 July 2026Patch Changes
Fix a one-frame viewport jump when above-viewport rows resize while scrolling up (#1227).
resizeItemwritesscrollTopsynchronously inside the ResizeObserver callback to compensate for the size change, but then notified asynchronously — so the browser could paint a frame with the newscrollTopand the old item transforms, making the content jerk by the resize delta and snap back. When a compensation actually moves the scroll position,resizeItemnow notifies synchronously so the transform commit lands in the same paint as the scroll write. Resizes that don't move the scroll position (below-fold measurements, iOS-deferred adjustments) keep the cheaper async notify.
3.17.6
22 July 2026Patch Changes
Stop the default scroll-adjustment heuristic from drifting the viewport when a viewport-spanning item grows. Previously any item whose top sat above the fold (
itemStart < scrollOffset) had its size delta compensated on every re-measure — including a streaming chat message that spans the fold and grows at its bottom, draggingscrollTopdownward token by token (#1218). Re-measurements now only compensate items that are entirely above the fold (itemStart + itemSize <= scrollOffset); growth below the anchor point leaves the scroll position untouched. First measurements (estimate→actual) still compensate any above-fold item, and a customshouldAdjustScrollPositionOnItemSizeChangestill overrides the default.
3.17.5
20 July 2026Patch Changes
Clamp the tracked
scrollOffsetat 0 when applying end-anchor measurement compensation and when re-anchoring insetOptions. Previously, withanchorTo: 'end'and content shorter than the viewport, items measuring smaller than their estimates drove the tracked offset negative with no scroll event to ever correct it —getDistanceFromEnd()reported a permanent phantom distance and iOS deferred measurement corrections stayed wedged forever.Stop iOS-deferred scroll adjustments from replaying stale deltas after the position is already correct (#1233). On iOS WebKit the end-anchored virtualizer defers scroll compensation while the scroller is live and replays it once settled, but two cases replayed a delta whose premise no longer held:
Absolute scroll commands (
scrollToOffset/scrollToIndex/scrollToEnd) derive their target from current measurements, so a pending deferred delta is already stale — it now invalidates the deferral instead of letting it replay and shift the list off the just-established position. Relative commands (scrollBy) keep the deferral.At the end clamp with
anchorTo: 'end', a row above the viewport re-measuring smaller lets the browser clampscrollToponto the new bottom (already the correct position); the flush now drops the stale negative compensation instead of replaying it and lifting the view off the bottom. Positive deltas still replay, since content growth above does not clamp.
3.17.4
12 July 20263.17.3
30 June 20263.17.2
26 June 20263.17.1
15 June 20263.17.0
2 June 20263.16.1
1 June 20263.16.0
25 May 20263.15.0
20 May 20263.14.0
17 April 20263.13.23
16 March 20263.13.22
12 March 20263.13.21
6 March 20263.13.20
5 March 20263.13.19
23 February 20263.13.18
7 January 20263.13.17
6 January 20263.13.16
3 January 20263.13.15
3 January 20263.13.14
30 December 20253.13.13
8 December 20253.13.12
27 June 20253.13.11
23 June 20253.13.10
9 June 20253.13.9
20 May 20253.13.8
5 May 20253.13.7
5 May 20253.13.6
31 March 20253.13.5
24 March 20253.13.4
13 March 20253.13.3
12 March 20253.13.2
25 February 20253.13.1
25 February 20253.13.0
7 February 20253.12.1
7 February 20253.12.0
3 February 20253.11.3
27 January 2025