blazor-performance

v2026.09.25

Blazor rendering optimization, virtualization, lazy loading, caching, and memory management

GitHub
Install command
npx skhub add thelobbi/blazor-performance
Markdown
SKILL.md

Blazor Performance Optimization

Rendering Optimization

Use @key for list rendering

@foreach (var item in Items)
{
    <ItemCard @key="item.Id" Item="@item" />
}

Override ShouldRender

private bool _shouldRender = true;
protected override bool ShouldRender() => _shouldRender;

Virtualize large lists

<Virtualize Items="@_allItems" Context="item" ItemSize="50">
    <ItemRow Item="@item" />
</Virtualize>

@* With item provider for server-side paging *@
<Virtualize ItemsProvider="LoadItems" Context="item">
    <ItemRow Item="@item" />
</Virtualize>

@code {
    private async ValueTask<ItemsProviderResult<ItemDto>> LoadItems(
        ItemsProviderRequest request)
    {
        var result = await Service.GetPagedAsync(request.StartIndex, request.Count);
        return new ItemsProviderResult<ItemDto>(result.Items, result.TotalCount);
    }
}

Streaming SSR for slow data

@attribute [StreamRendering]

@if (_data is null)
{
    <LoadingSpinner />
}
else
{
    <DataDisplay Data="@_data" />
}

Caching Strategies

Output Caching (API endpoints)

app.MapGet("/api/products", async (AppDbContext db) =>
    await db.Products.AsNoTracking().ToListAsync())
    .CacheOutput(policy => policy.Expire(TimeSpan.FromMinutes(5)).Tag("products"));

Distributed Cache (Redis)

public sealed class CachedProductService(
    IProductRepository repo,
    IDistributedCache cache) : IProductService
{
    public async Task<ProductDto?> GetByIdAsync(int id, CancellationToken ct)
    {
        var cacheKey = $"product:{id}";
        var cached = await cache.GetStringAsync(cacheKey, ct);
        if (cached is not null)
            return JsonSerializer.Deserialize<ProductDto>(cached);

        var product = await repo.GetByIdAsync(id, ct);
        if (product is not null)
        {
            await cache.SetStringAsync(cacheKey,
                JsonSerializer.Serialize(product),
                new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) },
                ct);
        }
        return product;
    }
}

Memory Cache for component state

@inject IMemoryCache Cache

@code {
    protected override async Task OnInitializedAsync()
    {
        _categories = await Cache.GetOrCreateAsync("categories", async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
            return await CategoryService.GetAllAsync();
        });
    }
}

WASM Size Optimization

  • Enable trimming: <PublishTrimmed>true</PublishTrimmed>
  • Enable AOT compilation: <RunAOTCompilation>true</RunAOTCompilation>
  • Use lazy assembly loading for rarely-used features
  • Minimize NuGet packages in WASM project

SignalR Connection (Blazor Server)

  • Monitor circuit count with Aspire dashboard
  • Set reasonable circuit timeout: CircuitOptions.DisconnectedCircuitRetentionPeriod
  • Minimize state stored in circuit (use external state stores)
  • Use @rendermode InteractiveAuto for high-traffic pages to offload to WASM
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.25

Published

Sep 25, 2026

Category

Uncategorized

License

MIT

Source path

plugins/dotnet-blazor/skills/blazor-performance

Default branch

main

Latest commit

2f1269c

Tree SHA

629e050