Skip to content

Add InitialIndex parameter and ScrollToIndexAsync API to Virtualize<TItem>#66753

Open
ilonatommy wants to merge 11 commits into
mainfrom
scroll-to-index
Open

Add InitialIndex parameter and ScrollToIndexAsync API to Virtualize<TItem>#66753
ilonatommy wants to merge 11 commits into
mainfrom
scroll-to-index

Conversation

@ilonatommy
Copy link
Copy Markdown
Member

@ilonatommy ilonatommy commented May 20, 2026

Adds two pieces of public API to Virtualize<TItem> that let consumers control which item is visible at the top of the list:

  • InitialIndex parameter — opens the list at a given item on first interactive render.
  • ScrollToIndexAsync(int itemIndex, CancellationToken cancellationToken = default) — programmatically scrolls to an item at any time after first render.

Out-of-range values are silently clamped to the valid range.

Public API

namespace Microsoft.AspNetCore.Components.Web.Virtualization;

public sealed class Virtualize<TItem>
{
    [Parameter]
    public int InitialIndex { get; set; }

    public Task ScrollToIndexAsync(int itemIndex, CancellationToken cancellationToken = default);
}

Behavior

Scenario Result
InitialIndex = 500 set before first render List opens with item 500 at the top, no flash of item 0
InitialIndex < OverscanCount (e.g., 1, 5) List opens at the exact requested index
InitialIndex = 0 (default) No initial-scroll work — component opens at item 0
InitialIndex out of range Clamped to the valid range
InitialIndex changed at runtime Ignored — InitialIndex is a one-shot, not a live binding
ScrollToIndexAsync(itemIndex) Returns a Task that completes when the target is aligned to the start of the viewport
Caller cancels the token Task faults with OperationCanceledException
Second ScrollToIndexAsync call while first is in flight First call's Task completes normally (silently superseded); only the last call's target is honored ("last call wins")
Called before first interactive render Throws InvalidOperationException synchronously (not on the returned Task), with a message pointing at InitialIndex
Item count shrinks between window-move and DOM commit Target is re-clamped post-render and the scroll bails cleanly if the window no longer contains the target
User scrolls during the operation The user's scroll wins (best-effort alignment)

Fixes #26943 ,
Fixes #66328

@ilonatommy ilonatommy self-assigned this May 20, 2026
@ilonatommy ilonatommy requested a review from a team as a code owner May 20, 2026 14:09
Copilot AI review requested due to automatic review settings May 20, 2026 14:09
@ilonatommy ilonatommy added the area-blazor Includes: Blazor, Razor Components label May 20, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the Blazor Virtualize<TItem> component with two new public APIs—InitialIndex and ScrollToIndexAsync—to allow consumers to control the initial and subsequent scroll position of a virtualized list, including updated JS interop support and E2E coverage.

Changes:

  • Adds InitialIndex parameter and ScrollToIndexAsync(int, CancellationToken) API to Virtualize<TItem>, including render-commit coordination and “last call wins” cancellation semantics.
  • Extends Virtualize JS interop with an alignToItem function and corresponding C# interop wrapper.
  • Adds unit tests, BasicTestApp UI hooks, and E2E tests covering initial index and programmatic scrolling scenarios.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Components/Web/src/Virtualization/Virtualize.cs Implements InitialIndex and ScrollToIndexAsync, including window movement, render rendezvous, and anchor/IO suppression during active scroll.
src/Components/Web/src/Virtualization/VirtualizeJsInterop.cs Adds C# JS interop entry point for aligning to an item.
src/Components/Web.JS/src/Virtualize.ts Adds JS alignToItem implementation and refactors anchor-restore measurement helper.
src/Components/Web/src/PublicAPI.Unshipped.txt Declares the new public API surface for Virtualize<TItem>.
src/Components/Web/test/Virtualization/VirtualizeTest.cs Adds unit tests for pre-init behavior, clamping, and cancellation behavior.
src/Components/test/testassets/BasicTestApp/VirtualizationAnchorMode.razor Adds UI controls and handlers to exercise InitialIndex/ScrollToIndexAsync in the test app.
src/Components/test/testassets/BasicTestApp/VirtualizationAnchorModeWindowScroll.razor Adds UI controls/handlers for window-scroll scenarios.
src/Components/test/E2ETest/Tests/VirtualizationTest.cs Adds E2E coverage for ScrollToIndexAsync and InitialIndex behaviors (fixed/variable height, clamping, rapid calls, cancellation, window scroll).
Comments suppressed due to low confidence (1)

src/Components/Web/test/Virtualization/VirtualizeTest.cs:1000

  • This test only asserts the returned Task is non-null, but it doesn't await it. If ScrollToIndexAsync faults asynchronously, the test would still pass and may leave unobserved exceptions. Consider awaiting the Task (optionally with a timeout) to validate successful completion.
        Task task = null;
        await renderer.Dispatcher.InvokeAsync(() => { task = virtualize.ScrollToIndexAsync(99_999); });

        Assert.NotNull(task);
    }

Comment thread src/Components/Web/src/Virtualization/Virtualize.cs Outdated
Comment thread src/Components/Web/src/Virtualization/Virtualize.cs
Comment thread src/Components/Web/src/Virtualization/VirtualizeJsInterop.cs Outdated
Comment thread src/Components/Web.JS/src/Virtualize.ts
Comment thread src/Components/Web/test/Virtualization/VirtualizeTest.cs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/Components/Web/src/Virtualization/Virtualize.cs:629

  • Same issue as OnBeforeSpacerVisible: ignoring OnAfterSpacerVisible while _currentScrollCts is non-null blocks virtualization window updates during a long-running ScrollToIndexAsync, which can conflict with the documented behavior that user scroll should win. Prefer canceling the scroll operation or allowing these callbacks through (and treating them as user override) rather than returning early for the whole scroll duration.
    void IVirtualizeJsCallbacks.OnAfterSpacerVisible(float spacerSize, float spacerSeparation, float containerSize)
    {
        if (_pendingAnchorRestore || _currentScrollCts is not null)
        {
            return;
        }

Comment thread src/Components/Web/src/Virtualization/Virtualize.cs
Comment thread src/Components/Web/src/Virtualization/Virtualize.cs
Comment thread src/Components/Web/src/Virtualization/Virtualize.cs
Comment thread src/Components/Web/src/Virtualization/Virtualize.cs
Copy link
Copy Markdown
Contributor

@dariatiurina dariatiurina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only one question, but other than that and Ondrej's notes, everything looks good.

Comment thread src/Components/test/E2ETest/Tests/VirtualizationTest.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ScrollToItemAsync(int itemIndex) design for Virtualization Virtualize component Row Index Enhancements (API to scroll to item)

4 participants