Virtualization supports variable-height items#64964
Draft
ilonatommy wants to merge 26 commits intodotnet:mainfrom
Draft
Virtualization supports variable-height items#64964ilonatommy wants to merge 26 commits intodotnet:mainfrom
ilonatommy wants to merge 26 commits intodotnet:mainfrom
Conversation
This was referenced Jan 8, 2026
Open
When effectiveItemSize is 0 or very small, dividing spacerSize by it causes integer overflow, resulting in itemsBefore being set to near int.MaxValue. This caused all items to be skipped and nothing rendered. The fix adds a guard to fall back to ItemSize when effectiveItemSize <= 0.
…m + 1st callback should be processed immidiately.
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.
High level requirements:
#25058 (comment)
This is WIP. Sample changes are not part of the fix.
Visualization of how the sample works with the initial implementation (scrolling with the scroll & jumps with Ctrl + Home or Ctrl + End):
Working-Variable-Height.mp4
Description
Scenarios that we should consider (test/document/log as a separate issue):
Dynamic Height Changes After Initial Render - ✅ covered, test added (
DynamicContent_ItemHeightChangesUpdateLayout)If an item's height changes after it's been measured (e.g., image loads, async content expands, user expands an accordion), the cached measurement should not become stale. If an item that is out of view expands (e.g. item 2 when view is focused on items 10-14), current view does not shift.
Window/Container Resize - ✅ covered, tests added (
VariableHeight_ContainerResizeWorks)When the container width changes, text wrapping changes, causing item heights to change. The cached measurements become invalid.
Extreme Height Variance - ✅ covered, tests changed to cover x100 variation.
If items vary from 20px to 2000px, the average-based estimation for un-measured items can be wildly inaccurate, causing large scroll jumps.
Memory Pressure from Measurement Cache
_measuredHeightsdictionary grows unbounded. With 1 million items and user scrolling through all of them, we store 1 million floats. Very large datasets or long user sessions with extensive scrolling might be problematic.Scroll Position Stability (Scroll Anchoring)
When spacer heights recalculate after measurements update, the browser may "jump" even with overflow-anchor: none. Users might see content shift.
ItemsProviderwith Variable Heights - ✅ covered, async tests added, slow big load tested manually (see the video)Currently we test with
Items(synchronous). When using asyncItemsProvider, measurements happen as items load. If loading is slow, we might calculate wrong positions.Async-delay.mp4
CSS Transform/Scale on Container - ✅ covered in
VariableHeightAsync_CanScrollThroughItemsWe have
getCumulativeScaleFactor()but it only checks transform matrix. Other CSS that affects layout (zoom, perspective) might not be handled.RTL (Right-to-Left) Layout - ✅ covered in
VariableHeightAsync_CanScrollThroughItemsHeight calculations should be unaffected, but scroll behavior and spacer positioning might differ.
Horizontal Virtualization Interaction
If someone uses Virtualize inside a horizontal scroll container, our height-based logic doesn't apply. This is probably out of scope, but worth documenting.
Placeholder Height Mismatch - ✅ covered by the walking average that only initially falls back to
ItemSizePlaceholders use
ItemSizefor height. If actual items are much taller/shorter, there's a visual jump when the real item replaces the placeholder. What would happen with placeholders with heights very different from actual content?First Item at Non-Zero Index
Related to issue Virtualize first render has upper spacer #64029. If the first visible item isn't index 0, measurements may start from wrong indices.
Empty or Single-Item Lists - ✅ covered in
VariableHeightAsync_SmallItemCountsWorkItems Collection Mutations - ✅ covered, added test
VariableHeightAsync_CollectionMutationWorksIf items are added/removed from the
Itemscollection, cached measurements reference stale indices. Adding items at beginning, removing items in middle etc.Test on Multiple Browsers ✅
Different browsers may fire callbacks at slightly different thresholds or with different batching. Chrome, Firefox, Safari all throttle scroll events differently.
getBoundingClientRect()returns sub-pixel values that differ by browser.scrollTopmay round differently (integer vs float).IntersectionObserveris a core of virtualization feature. it's mostly standarized acros browsers but edge cases might exist. It would be good to be aware of them.Result: Tested on Chrome, Firefox, and WebKit — all browsers behave consistently for virtualization: same visible item counts, exact scroll position accuracy, and integer pixel height measurements. The only notable difference is WebKit's 2x device pixel ratio (simulating Retina), which results in one additional sub-pixel item being rendered (22 vs 21).
Contributes to #25058.