Skip to content

Extras

Virtualize

Bit.BlazorUI.ExtrasVirtualScrollWindowing

BitVirtualize is a virtualization (windowing) component: it renders only the items currently in its scroll viewport, plus a small overscan buffer, so lists of millions of items scroll as smoothly as a few dozen. Items can have one fixed size or be measured as they render, come from memory or be loaded window by window, and scroll vertically or horizontally. It also offers header and footer content, sticky group headers, infinite scrolling in both directions, a bottom-anchored chat mode that keeps the view steady as data arrives, programmatic scrolling and full keyboard and screen-reader support.

Notes

To use this component, you need to install the Bit.BlazorUI.Extras(opens in a new tab) nuget package, as described in the Optional steps of the Getting started page.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

The simplest setup: an in-memory Items collection and a fixed ItemSize (the size of every item in pixels along the scroll axis). Only the rows in view, plus OverscanCount extra rows on each side (3 by default), exist in the DOM, so this list of one million rows scrolls instantly to anywhere. The list scrolls inside its own element, so give it a height (here through a CSS class).

ItemsProvider

Instead of Items, an ItemsProvider function loads the data window by window. Whenever the rendered range moves, it receives a request with the StartIndex and Count of the items needed and returns them along with the TotalItemCount. A request made obsolete by further scrolling gets its CancellationToken cancelled, and loaded items are cached, so scrolling back does not load them again.

LoadingTemplate shows until the first window arrives. After that, PlaceholderTemplate fills the slot of every item still loading; it receives the item's Index and reserved Size. While a request is in flight, the list is marked aria-busy for assistive technologies. A simulated latency of 500ms makes both templates visible. With more than one template, the item template is given as ItemTemplate, the alias of the ChildContent.

Loading

Dynamic size

When items cannot share one size, turn on Dynamic. Every rendered item is then measured in the browser and its real size is remembered, while items not rendered yet are assumed to be EstimatedItemSize pixels (the closer to the average, the more accurate the scrollbar). When an item above the viewport turns out bigger or smaller than estimated, the scroll position is corrected so the content you are reading never jumps.

Horizontal

Horizontal lays the items out along the x-axis so the list scrolls sideways, and ItemSize becomes the width of each item. Everything else (dynamic sizes, providers, sticky items and the scrolling methods) works the same way.

Scrolling

InitialIndex opens the list at a given item (item 500 here). From code, ScrollToIndexAsync brings an item into view with an alignment: Start, Center, End, or Auto, which moves as little as possible and not at all when the item is already visible. It can also animate smoothly. ScrollToOffsetAsync and ScrollByAsync work in pixels, and ScrollToStartAsync / ScrollToEndAsync go to the very edges. A scroll requested before the list is ready, for example while its data is still loading, is applied as soon as it is.

OnVisibleRangeChanged reports the index of the first visible item and the index just past the last one whenever they change.


[0, 20) visible

Keyboard & accessibility

Every rendered item carries its position in the whole list (aria-posinset and aria-setsize), so a screen reader announces "article 12 of 10,000" even though only a few items exist in the DOM. By default the list is a list of listitems. Role and ItemRole change that, here to the feed pattern of articles, and AriaLabel names the list.

Tab into the list and use the arrow keys, PageUp / PageDown, Home and End. Focus moves between the items and only the focused one is tabbable (a roving tabindex). The list scrolls to keep it in view, even when it jumps to an item that was not rendered yet. Keys pressed inside an item's own controls are left to them.

Templates

HeaderTemplate and FooterTemplate render before the first item and after the last one, inside the scroll container, so they scroll along with the items; every offset (scrolling methods, sticky items, the scroll anchoring) takes their size into account. EmptyTemplate replaces the items when there are none; empty and refill the list with the buttons.


Header · 1,000 items

Infinite scrolling

This feed has no known total. OnEndReached fires when the last item comes within ReachedThreshold items of the visible window, and it fires only once per item count. The handler appends the next batch to the collection and calls RefreshDataAsync (passing a new collection works as well), which lets it fire again the next time the end comes into view. The FooterTemplate shows the loading state at the end of the list.

Sticky headers

The contacts are grouped A-Z. IsStickyItem marks the group headers: the header of the current group stays pinned to the top of the viewport while its group scrolls, and the next header pushes it out as it arrives. StickyTemplate can render the pinned copy differently (here with a shadow). The pinned copy is hidden from assistive technologies, which still find the real header in its place in the list.

ItemKey

Without an ItemKey, the rendered rows are identified by their index. Inserting an item then hands every following row's DOM element and component state to a different item. With an ItemKey, each row keeps its own state (tick a few checkboxes, then add or remove tasks), measured sizes follow their items, and the item at the top of the viewport stays in place when items are added or removed above it. Scroll down a little first to see that: at the very top the list stays at the top and shows the new items.


Reversed (chat)

With Reversed the list starts at the newest message, and while you are at the bottom it stays there as messages arrive. Scroll up and OnStartReached prepends older history while the viewport stays exactly where it was (with an ItemKey, the message in view is tracked across the insertion). New messages arriving while you read the history do not move it; the button brings you back down with ScrollToEndAsync.

AlignToEnd keeps a conversation too short to fill the viewport at the bottom, like a chat app does. Start a new conversation to see it.

Scroll up for older messages

Style & Class

Style and Class apply to the root element, which is also the scroll container. Styles and Classes reach the individual parts: the root, the header and footer, the wrapper of every item, the pinned sticky item, and the loading and empty containers.

Style & Class:



Styles:

Header


Classes:

RTL

With Dir set to Rtl, a horizontal list starts at the right edge and scrolls to the left, and the arrow keys follow the reading direction (ArrowLeft moves to the next item).

API

Every parameter, public member, sub-class and enum this component exposes.

BitVirtualize parameters

Name Type Default value Description
AlignToEnd bool false Pushes the items to the end (bottom, or the right in horizontal mode) of the viewport while they are too few to fill it, the way a chat conversation starts at the bottom. Pairs naturally with Reversed.
ChildContent RenderFragment<TItem>? null The custom template to render each item.
Classes BitVirtualizeClassStyles? null Custom CSS classes for different parts of the BitVirtualize.
Dynamic bool false Enables dynamic item sizing in which each rendered item gets measured in the browser and its real size gets cached, using the EstimatedItemSize for the items that have not been measured yet.
EmptyTemplate RenderFragment? null The custom template to render when there is no item available.
EstimatedItemSize float 50 The assumed size in pixels of the items that have not been measured yet in dynamic mode.
FooterTemplate RenderFragment? null The custom template to render after the last item, inside the scroll container (for example, a loading indicator at the end of an infinite list).
HeaderTemplate RenderFragment? null The custom template to render before the first item, inside the scroll container.
Horizontal bool false Renders the items horizontally so the viewport scrolls along the x-axis.
InitialIndex int? null The index of the item to scroll to on the first render. Ignored when Reversed is set.
IsStickyItem Func<TItem, bool>? null A predicate that marks certain items (for example, group headers) as sticky. The active sticky item gets pinned to the leading edge of the viewport while its group scrolls. Fully supported with in-memory Items; in provider mode it is applied on a best-effort basis to the currently loaded window. A change in the state the predicate reads, rather than in the predicate itself, gets applied by RefreshDataAsync.
Items ICollection<TItem>? null The in-memory collection of items to virtualize. Mutually exclusive with ItemsProvider.
ItemKey Func<TItem, object>? null A function that returns a stable and unique identity key for an item. When provided, rendered rows are keyed by identity (instead of by index) so per-item DOM/component state survives insertions, removals and reordering, dynamic measurements follow their item across those mutations, and the item in view stays in place when items get inserted or removed before it.
ItemRole string? listitem The ARIA role of each item element.
ItemSize float 50 The size in pixels of each item along the scroll axis when the Dynamic mode is off.
ItemsProvider BitVirtualizeItemsProvider<TItem>? null The item provider function that lazily supplies windows of items on demand. Mutually exclusive with Items.
ItemTemplate RenderFragment<TItem>? null Alias for ChildContent.
LoadingTemplate RenderFragment? null The custom template to render until the component has performed its first load.
OnEndReached EventCallback The callback to be called when the last item comes within ReachedThreshold items of the visible window, useful for appending more data in infinite scrolling scenarios. Fires once per item-count value.
OnStartReached EventCallback The callback to be called when the first item comes within ReachedThreshold items of the visible window, useful for prepending older data (for example, loading chat history when scrolling up). Fires again when items get prepended while the start is still within reach.
OnVisibleRangeChanged EventCallback<(int Start, int End)> The callback to be called whenever the visible index range changes.
OverscanCount int 3 The number of extra items to render on each side of the visible window for smoother scrolling.
PlaceholderTemplate RenderFragment<BitVirtualizePlaceholderContext>? null The custom template to render an item whose data has not been loaded yet in provider mode.
ReachedThreshold int 0 The number of items away from an edge the visible window must be before OnEndReached/OnStartReached fire.
Reversed bool false Enables the bottom-anchored mode in which the list starts scrolled to the end and automatically keeps the newest items in view when data gets appended while the user is at the bottom. Ideal for chat and log views.
Role string? list The ARIA role of the root element.
StickyTemplate RenderFragment<TItem>? null The custom template to render the pinned sticky item. Falls back to the item template when not provided.
Styles BitVirtualizeClassStyles? null Custom CSS styles for different parts of the BitVirtualize.

BitVirtualize public members

Name Type Default value Description
RefreshDataAsync Func<Task> Re-requests the data from the ItemsProvider (or re-reads the Items) and refreshes the view.
ScrollToIndexAsync Func<int, BitVirtualizeScrollAlignment, bool, Task> Scrolls the viewport so that the item at the provided index becomes visible. A call made before the component is ready (for example, before its data arrives) gets applied once it is.
ScrollToOffsetAsync Func<double, bool, Task> Scrolls to an absolute pixel offset along the scroll axis, measured from the start of the first item.
ScrollByAsync Func<double, bool, Task> Scrolls the viewport by the provided number of pixels along the scroll axis (negative values scroll back).
ScrollToStartAsync Func<bool, Task> Scrolls to the very start (top/left) of the list, including the HeaderTemplate.
ScrollToEndAsync Func<bool, Task> Scrolls to the very end (bottom/right) of the list, including the FooterTemplate. Useful for chat and log views.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters.
Id string? null Gets or sets the unique identifier for the component's root element.
IsEnabled bool true Gets or sets a value indicating whether the component is enabled and can respond to user interaction.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitVirtualizeItemsProviderRequest properties

The request passed to the ItemsProvider function for a window of items.

Name Type Default value Description
StartIndex int 0 The zero-based index of the first item requested.
Count int 0 The maximum number of items requested.
CancellationToken CancellationToken A token that is cancelled when this request is no longer needed.

BitVirtualizeItemsProviderResult<TItem> properties

The result returned from the ItemsProvider function.

Name Type Default value Description
Items IReadOnlyList<TItem> The items that were loaded for the requested window.
TotalItemCount int 0 The total number of items in the underlying data source.

BitVirtualizePlaceholderContext properties

The context passed to the PlaceholderTemplate while real items are being loaded.

Name Type Default value Description
Index int 0 The zero-based index of the item this placeholder represents.
Size double 0 The estimated size (px) reserved for the placeholder along the scroll axis.

BitVirtualizeClassStyles properties

Custom CSS classes/styles for the different parts of the BitVirtualize.

Name Type Default value Description
Root string? null Custom CSS classes/styles for the root (scroll container) element of the BitVirtualize.
Header string? null Custom CSS classes/styles for the header container of the BitVirtualize.
Item string? null Custom CSS classes/styles for the wrapper element of each rendered item (and placeholder) of the BitVirtualize.
Sticky string? null Custom CSS classes/styles for the pinned sticky item container of the BitVirtualize.
Footer string? null Custom CSS classes/styles for the footer container of the BitVirtualize.
Loading string? null Custom CSS classes/styles for the loading container of the BitVirtualize.
Empty string? null Custom CSS classes/styles for the empty container of the BitVirtualize.

BitVirtualizeScrollAlignment enum

Name Value Description
Auto 0 Scroll the minimum amount required to bring the item fully into view.
Start 1 Align the item to the start (top/left) of the viewport.
Center 2 Center the item within the viewport.
End 3 Align the item to the end (bottom/right) of the viewport.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.