Skip to content

2.6.0 release - #928

Open
bvaughn wants to merge 3 commits into
mainfrom
bvaughn/2.6.0
Open

bvaughn wants to merge 3 commits into
mainfrom
bvaughn/2.6.0

Conversation

@bvaughn

@bvaughn bvaughn commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner
  • useDynamicRowHeight stores all of the rows measured at once (e.g. a newly visible page of rows) in a single update, rather than copying its cache for each row.
  • Lists with dynamic row heights keep the rows on screen in place when rows above them change height, e.g. as rows are measured while scrolling up (such as through a chat log), or when a row's content resizes. The scroll position is adjusted in the same frame, so rows never visibly jump. It isn't adjusted while the list is scrolled to the end or while scrollToRow is scrolling. While the user is touch scrolling, the rows are shifted instead (iOS ignores changes to the scroll position during touch scrolling), and the scroll position is adjusted once scrolling stops.
  • With dynamic row heights and a rowKey (or useVirtualizer's new getItemKey option), measured heights are stored by key, so they follow their rows when rows are inserted, removed, or reordered; and the rows on screen stay in place when rows are inserted or removed above them, e.g. when older messages are loaded at the top of a chat (including while scrolled to the top or end of the list), or when the data is filtered or re-sorted (as long as a row in view is still in the list). Apps that adjusted the scroll position themselves after inserting rows should stop doing so when they pass a key. Without a key, heights are stored by index as before. DynamicRowHeight's getRowHeight and setRowHeight methods accept an optional key, and observeRowElements the list's rows ({ count, getKey }); custom implementations that ignore them keep working (by index). Heights of rows that are no longer in the list are removed once the cache holds more than twice as many rows as the list has had (and at least 1,000), so rows hidden for a while (e.g. by a filter) keep their heights. The RowKeys type is exported for custom DynamicRowHeight implementations. Keys must be stable (not the index or a random value), and the function should be cheap, since it's called for many rows.
  • Add anchorRow method to the List imperative API (and anchorItem to useVirtualizer) for keeping a specific row where it is on screen while its height changes, e.g. when the user expands or collapses it. Call it before making the change; the row is kept in place for the specified duration (1 second by default). (By default, the list keeps the first visible row that didn't change height in place, so a row at the top of the viewport that grows would extend upward.)

Lists with dynamic row heights now adjust the scroll position when rows
above the viewport change height (e.g. as rows are measured while
scrolling up through a chat log, or when a row's content resizes), so
the rows in view don't move. Corrections are made in the same commit
that moves the rows, so no frame shows them out of place. Lists
scrolled to the end, or being scrolled by scrollToRow, are left alone.

While the user is touch scrolling (which iOS doesn't allow the scroll
position to be changed during), rows are shifted instead and the shift
is replaced by a scroll position change once scrolling stops.

Also adds List anchorRow (and useVirtualizer anchorItem) for keeping a
specific row in place while its height changes, e.g. when expanding it.
With dynamic row heights and a rowKey (or useVirtualizer's new getItemKey
option), measured heights are stored by key, so they follow their rows
when rows are inserted, removed, or reordered. The rows on screen stay
in place when rows are inserted or removed above them (e.g. when older
messages are loaded at the top of a chat), including at the top or end
of the list, and when the data is filtered or re-sorted (as long as a
row in view is still in the list).

The keys of the rendered rows are recorded after each commit, so the
anchor row can be found again by key after the data changes; a bounded
search near its previous index is tried first, falling back to a key
map for rows that moved farther. anchorRow requests follow their row
(or are cancelled if it's removed). Corrections the content wasn't
large enough for (e.g. right after an insert) are finished after
re-rendering, before the browser paints.

Without a key, heights are stored by index as before.
useDynamicRowHeight stores all of the rows measured by one ResizeObserver
callback in a single update, copying its cache once rather than once
per row.

Heights stored by key are removed once the cache holds more than twice
as many rows as the list has had (and at least 1,000), so the cache
doesn't grow without bound as rows come and go, while rows hidden for
a while (e.g. by a filter) keep their heights. observeRowElements now
takes the list's rows ({ count, getKey }); the RowKeys type is exported
for custom DynamicRowHeight implementations.
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
react-window Ready Ready Preview Oct 29, 2026 12:39am UTC

This branch was successfully deployed

1 active deployment
Preview — 47fc27cc Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant