Blazor DataGrid with Virtual Scrolling — Handle 100K+ Rows at 60fps
Loading 100,000 rows into a Blazor DataGrid without virtual scrolling will freeze the browser. The DOM can’t handle that many elements, and Blazor’s diffing engine chokes on the render tree. Arcadia DataGrid solves this with true virtual scrolling — only the visible rows exist in the DOM, giving you smooth 60fps performance regardless of dataset size.
Why Standard Blazor Grids Struggle
Blazor’s built-in <Virtualize> component is great for simple lists, but it wasn’t designed for data grids. It doesn’t handle:
- Fixed headers that stay visible during scroll
- Column pinning with synchronized scroll
- Row selection across virtualized and non-rendered rows
- Sorting and filtering without re-rendering everything
- Consistent row heights with mixed content
Arcadia DataGrid uses AG Grid’s proven virtualization engine under the hood, exposed through a clean Blazor API.
Quick Start
dotnet add package Arcadia.DataGrid
dotnet add package Arcadia.Theme
Add CSS to your App.razor:
<link href="_content/Arcadia.Theme/css/arcadia.css" rel="stylesheet" />
<link href="_content/Arcadia.DataGrid/css/arcadia-datagrid.css" rel="stylesheet" />
Enable Virtual Scrolling
@using Arcadia.DataGrid.Components
<ArcadiaDataGrid TItem="LogEntry"
Items="@logEntries"
VirtualizeRows="true"
Height="600px"
ItemSize="40"
OverscanCount="10"
Sortable="true"
Filterable="true">
<ArcadiaGridColumn Field="@nameof(LogEntry.Timestamp)" Header="Time" Format="G" Width="200" />
<ArcadiaGridColumn Field="@nameof(LogEntry.Level)" Header="Level" Width="100" />
<ArcadiaGridColumn Field="@nameof(LogEntry.Source)" Header="Source" Width="200" />
<ArcadiaGridColumn Field="@nameof(LogEntry.Message)" Header="Message" />
</ArcadiaDataGrid>
@code {
private List<LogEntry> logEntries = new();
protected override async Task OnInitializedAsync()
{
// Load 100K+ rows — the grid handles it
logEntries = await LogService.GetAllEntriesAsync();
}
}
Key parameters:
VirtualizeRows— enables row virtualizationHeight— required; the grid needs a fixed viewport heightItemSize— row height in pixels (default: 40)OverscanCount— extra rows rendered above/below viewport (default: 5)
Server-Side Virtual Scrolling
For truly massive datasets, load data on demand as the user scrolls:
<ArcadiaDataGrid TItem="Transaction"
ItemsProvider="@LoadTransactions"
VirtualizeRows="true"
Height="600px"
TotalItemCount="@totalCount">
<ArcadiaGridColumn Field="@nameof(Transaction.Id)" Header="ID" Width="100" />
<ArcadiaGridColumn Field="@nameof(Transaction.Date)" Header="Date" Format="d" />
<ArcadiaGridColumn Field="@nameof(Transaction.Amount)" Header="Amount" Format="C2" />
<ArcadiaGridColumn Field="@nameof(Transaction.Status)" Header="Status" />
</ArcadiaDataGrid>
@code {
private int totalCount = 500_000;
private async ValueTask<ItemsProviderResult<Transaction>> LoadTransactions(
ItemsProviderRequest request)
{
var results = await TransactionService.GetPageAsync(
request.StartIndex, request.Count, request.CancellationToken);
return new ItemsProviderResult<Transaction>(results, totalCount);
}
}
The grid only fetches the rows currently in view, keeping memory usage flat.
Performance Benchmarks
Tested on a mid-range laptop (i5-1240P, 16 GB RAM, Chrome 126):
| Row Count | Initial Render | Scroll FPS | Memory (DOM) |
|---|---|---|---|
| 1,000 | 45 ms | 60 fps | 2.1 MB |
| 10,000 | 48 ms | 60 fps | 2.1 MB |
| 100,000 | 52 ms | 60 fps | 2.2 MB |
| 500,000 | 55 ms | 59 fps | 2.3 MB |
| 1,000,000 | 60 ms | 58 fps | 2.4 MB |
Initial render time stays nearly constant because only ~30 rows are ever in the DOM.
How It Compares
| Arcadia | Blazor Virtualize | MudBlazor | Radzen | Syncfusion | |
|---|---|---|---|---|---|
| Max rows tested | 1M+ | 100K | 50K | 100K | 500K |
| Fixed headers | Yes | No | Yes | Yes | Yes |
| Column pinning | Yes | No | No | Yes | Yes |
| Sort + virtualize | Yes | Manual | Partial | Yes | Yes |
| Filter + virtualize | Yes | Manual | Partial | Yes | Yes |
| Server-side paging | Yes | Yes | No | Yes | Yes |
| Configurable overscan | Yes | Yes | No | No | Yes |
| Smooth scroll | 60 fps | 55-60 fps | 40-50 fps | 55 fps | 55 fps |
| Price | $299/dev/yr | Built-in | Free (MIT) | Free tier | $995+/year |
Tips for Best Performance
- Set
ItemSizeaccurately — mismatched row heights cause scroll jank - Use
OverscanCount="10"— prevents blank flashes during fast scrolling - Avoid complex templates — keep cell renderers lightweight
- Use server-side mode for 500K+ rows — don’t load everything into memory
- Pin key columns — pinned columns render in a separate viewport for smooth horizontal scroll
Try It
- Live Demo — test with 100K rows in the interactive playground
- Documentation — full virtualization API reference
- GitHub — source code (MIT)
dotnet add package Arcadia.DataGrid
Virtual scrolling is included in the Arcadia DataGrid Pro package ($299/dev/year). View pricing.