Skip to content

[virtual-core] followOnAppend loses end position when appending and trimming together #1271

Description

@tigerBeA

Describe the bug

With anchorTo: 'end' and followOnAppend: true, appending a new item while trimming the oldest item in the same update moves an end-pinned viewport away from the bottom when the count stays unchanged.

The reproduction starts with 20 messages, each 50 px tall, in a 300 px viewport. Changing messages 1–20 to 2–21 keeps the total height at 1000 px, but moves scrollTop from 700 to 650, leaving a 50 px bottom gap. Appending without trimming follows correctly.

Your minimal, reproducible example

Live window-follow reproduction

Pinned source · Source and local run instructions

Published packages: @tanstack/react-virtual@3.14.11, @tanstack/virtual-core@3.17.9, and React 19.2.8. Message IDs are persistent, and the memoized getItemKey callback captures each immutable message array. Native CSS scroll anchoring is disabled. There is no streaming, network, dynamic measurement, or custom scroll compensation.

Steps to reproduce

  1. Open the page and confirm Actual bottom gap: 0.00 px.
  2. Click Append + trim oldest once. The array changes from messages 1–20 to 2–21 in one state update.
  3. Observe Actual bottom gap: 50.00 px. The latest message is below the viewport.
  4. Click Reset to repeat.
  5. As a control, reset and click Append only (control). The count grows to 21 and the bottom gap remains 0 px.

Expected behavior

A viewport already within scrollEndThreshold of the end should stay at the end when new items are appended and older items are trimmed in the same update. In this example scrollTop should remain 700 and the bottom gap should remain 0.

A user reading earlier content should retain their reading position. For these non-growing updates, a replacement, reorder, or trim without new appended items should not be treated as appended output merely because the last key changes. This report does not request a change to the existing count-growing classification.

How often does this bug happen?

Every time with the steps above.

Screenshots or Videos

Append and trim leaves a 50 px bottom gap

Measured from the real scroll element:

Scenario Count scrollHeight clientHeight scrollTop Bottom gap
Initial 20 1000 300 700 0
Append + trim 20 1000 300 650 50
Append only, after reset 21 1050 300 750 0

Platform

  • macOS 26.5.2
  • Chrome 152 (reported user-agent major version)
  • Verified on 2026-09-08 using a production Vite build

tanstack-virtual version

@tanstack/react-virtual@3.14.11 / @tanstack/virtual-core@3.17.9

TypeScript version

Not applicable; the reproduction uses JavaScript/JSX.

Additional context

Proposed fix: #1272.

Related: #1270 describes inconsistent lazy measurement keys with a stable callback. This reproduction uses a callback that captures each immutable array and still loses end following, so the two behaviors can be reproduced independently.

The core's append-follow condition in setOptions requires nextCount > prevCount. This update changes the edge keys without increasing the count, so it takes the visible-item anchoring path instead of following the new tail.

The reproduction retains an ordered, non-empty suffix of the old list and appends a new key. It does not rely on treating wholesale replacement or recycled keys as an append.

Terms & Code of Conduct

  • I agree to follow this project's Code of Conduct.
  • I understand that if my bug cannot be reliably reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions