fix(scroll): keep autoscroll to bottom when item sizes settle - #2457
Open
dennytosp wants to merge 1 commit into
Open
fix(scroll): keep autoscroll to bottom when item sizes settle#2457dennytosp wants to merge 1 commit into
dennytosp wants to merge 1 commit into
Conversation
useBoundDetection skips the autoscroll check when the content size changes within 100ms of the last bounds check, so an active scroll is not fought. The check is dropped rather than deferred, and the taller content then pushes the bottom out of reach, so the next checkBounds clears the pending autoscroll and nothing ever brings it back. Items measuring to their real height a frame after they mount is exactly that case, which is why a list with dynamic item heights stops sticking to the bottom: an image finishing its load or a message wrapping onto another line is enough to lose it. Defer the check to the moment the scroll goes quiet instead of dropping it, and run the autoscroll that was owed. The deferred run is cancelled if the scroll offset moved in the meantime, which is what separates the two cases: content growing below the viewport leaves the offset untouched, while a user who takes over changes it and keeps ownership of the position. The deferred run goes through runAutoScrollToBottomCheck rather than resurrecting the pending flag, so scrollToEnd stays suppressed while offset projection is disabled and no stale flag is left behind for a later data change to fire. Fixes Shopify#1903
dennytosp
force-pushed
the
fix/autoscroll-to-bottom-size-change
branch
from
August 20, 2026 05:37
f1b9ca8 to
a0c4fbf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1903
Related: #1666
Problem
A chat-style list with
maintainVisibleContentPosition.autoscrollToBottomThresholdstops sticking to the bottom as soon as item heights settle after the row mounts — an image finishing its load, a link preview appearing, a message wrapping onto another line.useBoundDetectionruns the autoscroll check on content size changes, but guards it so it does not fight an active scroll:Inside that 100ms window the check is dropped, not deferred — and it never comes back:
checkBounds→ skippedcheckBoundscomputesisNearBottom === falseand clearspendingAutoscrollToBottomItem heights settling a frame after new content arrives lands squarely inside that window, which is why this reproduces so consistently with dynamic-height rows.
Fix
Defer instead of drop. When the size change arrives mid-scroll, wait out the same 100ms and then run the autoscroll that was owed.
The deferred run is cancelled if the scroll offset moved in the meantime. That is the signal that separates the two cases, and it is a direct measurement rather than a heuristic:
checkBoundshas already recorded whether they are still near the bottom, so leave the decision to it and do not yank them back downThe deferred run goes through
runAutoScrollToBottomCheck(with aforceflag) rather than resurrectingpendingAutoscrollToBottom, so it shares the existingisOffsetProjectionEnabledguard — ascrollToIndexin flight is still not hijacked — and no stale flag is left behind for a later data change to fire.Tests
Four tests in
RecyclerView.test.tsxunderautoscroll to bottom when item sizes change:Each guard was mutation-tested rather than assumed:
A fifth test was written and then deleted: it asserted no autoscroll on top of a settling
scrollToIndex, and instrumenting it showed the deferred path was never entered — it passed with the guard removed, so it was testing nothing.Scope note
This is unit-verified. #1903 also involves
startRenderingFromBottomwith items prepended throughonStartReached, so there may be a second contributing factor in the MVCP anchoring on prepend; this change fixes the size-settling half, which is the part the report demonstrates and the part #1666 is entirely about.On #1666 — the reply there was that the feature is meant to scroll on data change, not on item size change. Worth noting the effect already depends on
contentHeight/contentWidth, so the size-change path exists in the code today; it is just lossy inside the guard window. This makes the existing path reliable rather than adding new behaviour. Happy to narrow it to data-change-only if you'd rather keep that boundary.