QuickGrid: High-Performance Data Grids in Blazor
Data grids are one of the most common UI components in business applications, and historically one of the weakest spots in Blazor's default offering. QuickGrid changes that. It's a built-in, high-performance grid component that handles sorting, pagination, and filtering with minimal ceremony.
Getting Started
QuickGrid is included in the Microsoft.AspNetCore.Components.QuickGrid package (bundled with .NET 8 web app templates). If you need to add it:
dotnet add package Microsoft.AspNetCore.Components.QuickGrid
The simplest grid takes an IQueryable<T> and column definitions:
@using Microsoft.AspNetCore.Components.QuickGrid
<QuickGrid Items="people">
<PropertyColumn Property="p => p.Name" Sortable="true" />
<PropertyColumn Property="p => p.Email" />
<PropertyColumn Property="p => p.Department" Sortable="true" />
<PropertyColumn Property="p => p.JoinDate" Format="dd MMM yyyy" Sortable="true" />
</QuickGrid>
@code {
private IQueryable<Person> people = new List<Person>
{
new("Alice Smith", "[email protected]", "Engineering", new DateTime(2022, 3, 15)),
new("Bob Jones", "[email protected]", "Marketing", new DateTime(2023, 1, 10)),
// ...
}.AsQueryable();
}
That's it. You get a styled, accessible table with sortable columns. No third-party packages, no JavaScript dependencies.
Column Types
PropertyColumn
The most common column type — it binds directly to a property:
<PropertyColumn Property="p => p.Name" Title="Full Name" Sortable="true" />
<PropertyColumn Property="p => p.Salary" Format="C2" Title="Annual Salary" />
The Format parameter accepts standard .NET format strings. Title overrides the auto-generated column header.
TemplateColumn
For custom rendering, use TemplateColumn:
<TemplateColumn Title="Actions">
<button @onclick="() => Edit(context)">Edit</button>
<button @onclick="() => Delete(context)">Delete</button>
</TemplateColumn>
<TemplateColumn Title="Status">
<span class="badge @(context.IsActive ? "badge-green" : "badge-red")">
@(context.IsActive ? "Active" : "Inactive")
</span>
</TemplateColumn>
The context variable is the row's data item.
Pagination
Add pagination with the PaginationState class:
<QuickGrid Items="products" Pagination="pagination">
<PropertyColumn Property="p => p.Name" Sortable="true" />
<PropertyColumn Property="p => p.Price" Format="C2" Sortable="true" />
<PropertyColumn Property="p => p.Category" Sortable="true" />
</QuickGrid>
<Paginator State="pagination" />
@code {
private PaginationState pagination = new() { ItemsPerPage = 20 };
private IQueryable<Product> products = default!;
protected override async Task OnInitializedAsync()
{
var allProducts = await ProductService.GetAllAsync();
products = allProducts.AsQueryable();
}
}
The Paginator component renders page navigation controls. It reads from and writes to the shared PaginationState.
Server-Side Data with ItemsProvider
For large datasets, you don't want to load everything into memory. Use ItemsProvider to fetch data on demand:
<QuickGrid ItemsProvider="LoadProducts" Pagination="pagination">
<PropertyColumn Property="p => p.Name" Sortable="true" />
<PropertyColumn Property="p => p.Price" Format="C2" Sortable="true" />
<PropertyColumn Property="p => p.Category" Sortable="true" />
</QuickGrid>
<Paginator State="pagination" />
@code {
private PaginationState pagination = new() { ItemsPerPage = 25 };
private async ValueTask<GridItemsProviderResult<Product>> LoadProducts(
GridItemsProviderRequest<Product> request)
{
var result = await ProductService.GetPagedAsync(
startIndex: request.StartIndex,
count: request.Count ?? 25,
sortBy: request.GetSortByProperties().FirstOrDefault()?.PropertyName,
ascending: request.GetSortByProperties().FirstOrDefault()?.Direction
== SortDirection.Ascending,
cancellationToken: request.CancellationToken);
return GridItemsProviderResult.From(
items: result.Items,
totalItemCount: result.TotalCount);
}
}
The ItemsProvider delegate receives sorting and pagination parameters, letting your backend handle the heavy lifting. This works beautifully with Entity Framework Core — pass the IQueryable and let EF translate sorting and pagination to SQL.
EF Core Integration
QuickGrid has first-class EF Core support through a separate package:
dotnet add package Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter
Register the adapter:
builder.Services.AddQuickGridEntityFrameworkAdapter();
Then pass your DbSet directly:
@inject ApplicationDbContext Db
<QuickGrid Items="Db.Products.Include(p => p.Category)" Pagination="pagination">
<PropertyColumn Property="p => p.Name" Sortable="true" />
<PropertyColumn Property="p => p.Category.Name" Title="Category" Sortable="true" />
<PropertyColumn Property="p => p.Price" Format="C2" Sortable="true" />
</QuickGrid>
QuickGrid translates sorting and pagination into LINQ operations that EF Core converts to efficient SQL queries.
Styling
QuickGrid renders semantic HTML (<table>, <thead>, <tbody>, <tr>, <td>) and applies CSS classes for sorting state. You can style it with standard CSS:
.quickgrid th.sort-asc::after { content: " ▲"; }
.quickgrid th.sort-desc::after { content: " ▼"; }
.quickgrid th[aria-sort] { cursor: pointer; }
.quickgrid tr:hover { background-colour: #f8fafc; }
There's also a default theme you can include:
<link rel="stylesheet" href="_content/Microsoft.AspNetCore.Components.QuickGrid/Microsoft.AspNetCore.Components.QuickGrid.bundle.ssd.css" />
When to Use Something Else
QuickGrid is intentionally focused. It handles sorting, pagination, and basic filtering well. If you need inline editing, column reordering, grouping, row selection, or Excel-style filtering, you'll want a more full-featured grid like Telerik, Syncfusion, or MudBlazor's MudDataGrid. QuickGrid is excellent for read-heavy display scenarios and as a starting point that avoids third-party dependencies.