From e0077fb642ae8bf2d7603a9679fa87799ecd6f3e Mon Sep 17 00:00:00 2001 From: Damian Pieczynski Date: Sat, 3 Jan 2026 15:11:52 +0100 Subject: [PATCH 1/2] fix(virtual-core): scroll to last index properly --- examples/react/dynamic/src/main.tsx | 1 + packages/virtual-core/src/index.ts | 31 +++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/react/dynamic/src/main.tsx b/examples/react/dynamic/src/main.tsx index 813901309..dd8aa4511 100644 --- a/examples/react/dynamic/src/main.tsx +++ b/examples/react/dynamic/src/main.tsx @@ -70,6 +70,7 @@ function RowVirtualizerDynamic() { width: 400, overflowY: 'auto', contain: 'strict', + overflowAnchor: 'none', }} >
{ + if (!this.scrollElement) return 0 + + if ('scrollHeight' in this.scrollElement) { + // Element + return this.options.horizontal + ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth + : this.scrollElement.scrollHeight - this.scrollElement.clientHeight + } else { + // Window + const doc = this.scrollElement.document.documentElement + return this.options.horizontal + ? doc.scrollWidth - this.scrollElement.innerWidth + : doc.scrollHeight - this.scrollElement.innerHeight + } + } + getOffsetForAlignment = ( toOffset: number, align: ScrollAlignment, itemSize = 0, ) => { + if (!this.scrollElement) return 0 + const size = this.getSize() const scrollOffset = this.getScrollOffset() @@ -992,7 +1011,7 @@ export class Virtualizer< toOffset -= size } - const maxOffset = this.getTotalSize() + this.options.scrollMargin - size + const maxOffset = this.getMaxScrollOffset() return Math.max(Math.min(maxOffset, toOffset), 0) } @@ -1014,10 +1033,18 @@ export class Virtualizer< } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) { align = 'start' } else { - return [scrollOffset, align] as const + // Item is already visible, return current position with concrete alignment + // to avoid infinite retry loop if measurements change + return [scrollOffset, 'start'] as const } } + // For the last item with 'end' alignment, use browser's actual max scroll + // to account for borders/padding that aren't in our measurements + if (align === 'end' && index === this.options.count - 1) { + return [this.getMaxScrollOffset(), align] as const + } + const toOffset = align === 'end' ? item.end + this.options.scrollPaddingEnd From a6a3bbb9e2342729c0c541f0ca25be3ebd9c2142 Mon Sep 17 00:00:00 2001 From: Damian Pieczynski Date: Sat, 3 Jan 2026 15:19:50 +0100 Subject: [PATCH 2/2] chore: add changeset --- .changeset/wild-chairs-drive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wild-chairs-drive.md diff --git a/.changeset/wild-chairs-drive.md b/.changeset/wild-chairs-drive.md new file mode 100644 index 000000000..2a4a3efe1 --- /dev/null +++ b/.changeset/wild-chairs-drive.md @@ -0,0 +1,5 @@ +--- +'@tanstack/virtual-core': patch +--- + +fix(virtual-core): scroll to last index properly