Blazor QuickGrid with WebAssembly and API backend – complete example, Part 1


Microsoft has created the QuickGrid component, which is in prerelease, experimental status for .NET 7 and will be released in production status for .NET 8. It can be used to display data in a table format quickly and efficiently.  QuickGrid is designed for common data grid scenarios and shows how to build data grid components with high performance. QuickGrid uses advanced techniques to optimise the rendering speed of the data grid.

Microsoft has put together a site containing many examples of how to use it at Unfortunately, they don’t explain how to implement all parts, including the backend API. This series of blog posts will cover that. I’ll also create a separate project to share models between the Blazor Webassembly and API applications.


We’ll start with a simple solution that contains two .NET 8 projects – QuickGrid.API and QuickGrid.Shared. The API will return a list of customers retrieved from the database. The customer data in the database will be changed to be represented by the Customer DTO in the Shared project. Having this DTO in the shared project means the domain model does not need to be serialised into the response, and it also allows us to share the same object with the Blazor Web application we will be building.

You can check out the code and switch to the Start branch to follow along. You’ll see the API simply returns an extensive list of customers. We will improve it to support paging, sorting and filtering as we implement QuickGrid.

Creating the Blazor WASM project

Start by adding a new Blazor Webassembly App to the project. I named mine QuickGrid.Web. Set the following options:

Delete the counter.razor page and remove its reference in the NavMenu.razor file. Run the project and confirm it looks and works as follows:

We will use the Fetch data page to add QuickGrid.

Calling the API

Before working with QuickGrid, let’s ensure we can retrieve the data from our API. Add a reference to the Shared project from the Blazor project.

Change the forecasts array to use the Customer model from the shared project, and also rename the array to customers. Change the method that gets the weather forecasts to get customers from our API now. You will find the address for the API in the launchSettings.json file of the API project You can also remove the WeatherForecast class that is declared in this file.

Next, change the table to display customer data instead of forecast data. You can also update the PageTitle and h1 tags to be Customers.

The complete code for FetchData.razor will now be:

@page "/fetchdata"
@using QuickGrid.Shared.Dtos;
@inject HttpClient Http



<p>This component demonstrates fetching data from the server.</p>

@if (customers == null)
    <table class="table">
                <th>Email Address</th>
                <th>User Name</th>
            @foreach (var customer in customers)
                    <td><img src="@customer.Avatar" width="50" alt="Customer Avatar" /></td>
@code {
    private Customer[]? customers;

    protected override async Task OnInitializedAsync()
        customers = await Http.GetFromJsonAsync<Customer[]>("https://localhost:7111/Customer");

In the API project, find the Program.cs file, and add the following code to the app.Environment.IsDevelopment() statement to allow CORS requests from our Blazor application. For production, you should configure the specific details from where your Blazor application will be making requests from.

app.UseCors(x => x
     .SetIsOriginAllowed(origin => true));

Finally, set the solution to open the API and Blazor application together.

Run the two projects, and you should see an extensive list of customer data when viewing the Fetch Data page. Unless you have a powerful system, you will likely notice the waiting time while the list is loaded.

Implementing QuickGrid

In the Blazor project, add a NuGet package reference to Microsoft.AspNetCore.Components.QuickGrid. Up until the release of NET 8, QuickGrid is only available in prerelease, so ensure this option is checked; otherwise, you will not see it in the list.

Add a using statement in FetchData.razor for Microsoft.AspNetCore.Components.QuickGrid. Next, replace the table HTML with the following code that will use the QuickGrid component.

<QuickGrid Items="@customers">
    <TemplateColumn Title="Avatar">
        <img width="50" src="@context.Avatar" alt="Customer avatar" />
    <PropertyColumn Property="@(p => p.Name)" />
    <PropertyColumn Property="@(p => p.EmailAddress)" />
    <PropertyColumn Property="@(p => p.UserName)" />

FInally, we have to change the code in the @code section to work with an IQueryable, which is what QuickGrid requires.

@code {
    private IQueryable<Customer>? customers;

    protected override async Task OnInitializedAsync()
        customers = Queryable.AsQueryable(await Http.GetFromJsonAsync<List<Customer>>("https://localhost:7111/Customer"));

When you run both projects and navigate to the FetchData page, you’ll see the QuickGrid component is now used. You might find your browser freezes while all the data loads – we’ll fix that with paging soon.

You can check out the code up to here by using the Implementing-QuickGrid branch.

Implement paging

To support paging; we need to create a PaginationState object and pass it to the Pagination property of the grid. We also need to set up the UI to allow users to select the page size and which page they want to view. All the changes in this section will be in the FetchData.razor file.

In the @code section, create a new variable to hold the pagination state:

private PaginationState paginationState = new PaginationState { ItemsPerPage = 10 };

Add the following code above the QuickGrid component to allow users to select the number of items to view per page.

    Items per page:
    <select @bind="@paginationState.ItemsPerPage">

Add a pagination attribute to the QuickGrid component, referencing the PaginationState.

<QuickGrid Items="@customers" Pagination="@paginationState">

Finally, add a Paginator component under the QuickGrid component to allow users to switch between pages.

<Paginator State="@paginationState" />

Run the API and Web projects together to see the results of these changes. The FetchData page will still take some time to load as it’s still requesting all the data from the API, but once loaded, it should be much more responsive.

Customise the pagination UI

The Paginator component default UI is not a terrible user experience, but you may want to customise it. Let’s see how to do that.

Remove the Paginator component and replace it with the following code.

<div class="text-center">
    <div class="btn-group">
        @if (paginationState.TotalItemCount.HasValue)
            if (paginationState.CurrentPageIndex > 0)
                <button class="btn btn-primary" @onclick="@(() => paginationState.SetCurrentPageIndexAsync(0))">

                <button class="btn btn-primary" @onclick="@(() => paginationState.SetCurrentPageIndexAsync(paginationState.CurrentPageIndex - 1))">

            <button class="btn btn-outline-primary">
                @(paginationState.CurrentPageIndex + 1)

            if (paginationState.CurrentPageIndex < paginationState.LastPageIndex)
                <button class="btn btn-primary" @onclick="@(() => paginationState.SetCurrentPageIndexAsync(paginationState.CurrentPageIndex + 1))">

                <button class="btn btn-primary" @onclick="@(() => paginationState.SetCurrentPageIndexAsync(paginationState.LastPageIndex.GetValueOrDefault()))">

This contains some simple UI button elements that will allow you to page through the grid. The previous and next buttons will only show if there are previous and next pages. The onclick handlers will set the current page of the paginationState object, which will update the grid.

In the @code section, add the following below where customers is assigned to ensure the pagination is updated when the total number of items changes.

paginationState.TotalItemCountChanged += (sender, eventArgs) => StateHasChanged();

Run the Web and API project, navigate to the Fetch data page and you should see the following:

You can access the code at the end of this step on the Implemented-Paging branch.

That’s all I’ll cover in this post to prevent it from getting too long. In the next post, we’ll go over virtualisation, filtering and sorting.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.