mcpField
B
blazor

blazor

github.com/managedcodeDevelopment49639 installsUpdated 1d ago
Repository

About blazor

A development skill for building interactive web interfaces with C# and Blazor. It guides render-mode selection, component design, state handling, prerendering, performance, and JavaScript interop, with guidance for connecting UI to AI backends.

Key features

  1. 01选择 Static、Server、WebAssembly 或 Auto 渲染模式
  2. 02设计可复用组件与状态管理
  3. 03处理预渲染和状态持久化
  4. 04使用 JavaScript 互操作
  5. 05优化列表渲染与性能

Use cases

  1. 01用 C# 构建交互式 Web 界面
  2. 02按 SEO、实时性或离线需求选择渲染模式
  3. 03避免预渲染与水合期间重复获取数据

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/managedcode/dotnet-skills --skill blazor

After installing, type/blazorin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

manifest.json
references/
anti-patterns.md
patterns.md

Details

From the project README

Blazor

Trigger On

  • building interactive web UIs with C# instead of JavaScript
  • choosing between Server, WebAssembly, or Auto render modes
  • designing component hierarchies and state management
  • handling prerendering and hydration
  • integrating with JavaScript when necessary
  • connecting Blazor UI with AI backends and building AI agents with .NET

Documentation

References

Render Modes (.NET 8+)

ModeWhere It RunsBest For
StaticServer (no interactivity)SEO pages, marketing content
InteractiveServerServer via SignalRReal-time apps, thin clients
InteractiveWebAssemblyBrowser via WASMOffline-capable, client-heavy
InteractiveAutoServer first, then WASMBest of both worlds
Applying Render Modes
@* Per-component *@
@rendermode InteractiveServer

@* Or in App.razor for global *@
<Routes @rendermode="InteractiveAuto" />
InteractiveAuto Architecture
First Request:
  Browser → Server (Interactive Server) → Fast response

Subsequent Requests:
  Browser → WASM (downloaded in background) → No server needed

Workflow

  1. Choose render mode based on requirements:

    • Need SEO? Start with Static or prerendering
    • Need real-time? Use InteractiveServer
    • Need offline? Use InteractiveWebAssembly
    • Want both? Use InteractiveAuto
  2. Design components for reusability:

    • Small, focused components
    • Parameters for customization
    • Events for communication
  3. Handle state correctly:

    • Component state lives in component
    • Shared state via services (DI)
    • Persist state across prerender with [PersistentState]
  4. Validate in both environments (for Auto mode)

Current Upstream Notes

  • Treat dotnet/aspnetcore v10.0.11 as servicing. It fixes restoration of expired client-persisted circuit state; re-run reconnect, persisted-state expiry, render-mode, and interactive-auto client/server split tests when upgrading.
  • The August 2026 ASP.NET Core overview still positions Blazor as the component UI path. A servicing update does not change the component or render-mode architecture by itself.

Component Patterns

Basic Component
@* Counter.razor *@
<button @onclick="IncrementCount">
    Clicked @count times
</button>

@code {
    private int count = 0;

    [Parameter]
    public int InitialCount { get; set; } = 0;

    protected override void OnInitialized()
    {
        count = InitialCount;
    }

    private void IncrementCount() => count++;
}
Parameter and Event Callbacks
@* Parent.razor *@
<ChildComponent Value="@value" ValueChanged="@OnValueChanged" />

@* ChildComponent.razor *@
@code {
    [Parameter] public string Value { get; set; } = "";
    [Parameter] public EventCallback<string> ValueChanged { get; set; }

    private async Task UpdateValue(string newValue)
    {
        await ValueChanged.InvokeAsync(newValue);
    }
}
State Persistence (.NET 8+)
@* Prevents double-fetch during prerender + hydration *@
@code {
    [PersistentState]
    public List<Product> Products { get; set; } = [];

    protected override async Task OnInitializedAsync()
    {
        // Only fetches once, persisted across prerender
        Products ??= await Http.GetFromJsonAsync<List<Product>>("api/products");
    }
}

Data Access Pattern for Auto Mode

// Shared interface
public interface IProductService
{
    Task<List<Product>> GetProductsAsync();
}

// Server implementation (direct DB access)
public class ServerProductService : IProductService
{
    private readonly AppDbContext _db;
    public async Task<List<Product>> GetProductsAsync()
        => await _db.Products.ToListAsync();
}

// Client implementation (HTTP call)
public class ClientProductService : IProductService
{
    private readonly HttpClient _http;
    public async Task<List<Product>> GetProductsAsync()
        => await _http.GetFromJsonAsync<List<Product>>("api/products");
}

// Registration
// Server: builder.Services.AddScoped<IProductService, ServerProductService>();
// Client: builder.Services.AddScoped<IProductService, ClientProductService>();

Anti-Patterns to Avoid

Anti-PatternWhy It's BadBetter Approach
Large componentsHard to maintain, slow rendersSplit into smaller components
Direct DB access in WASMNo DB in browserUse HTTP API
Ignoring ShouldRenderUnnecessary re-rendersOverride when needed
Sync JS interop in ServerBlocks SignalR circuitUse IJSRuntime async
No error boundariesOne error crashes appUse <ErrorBoundary>
Forgetting prerender stateDouble API callsUse [PersistentState]

Performance Best Practices

  1. Virtualize large lists:

    <Virtualize Items="@products" Context="product">
        <ProductCard Product="@product" />
    </Virtualize>
    
  2. Use @key for list diffing:

    @foreach (var item in items)
    {
        <ItemComponent @key="item.Id" Item="@item" />
    }
    
  3. Debounce rapid events:

    private Timer? _debounceTimer;
    
    private void OnInput(ChangeEventArgs e)
    {
        _debounceTimer?.Dispose();
        _debounceTimer = new Timer(_ => InvokeAsync(DoSearch), null, 300, Timeout.Infinite);
    }
    
  4. Lazy load assemblies (WASM):

    var assemblies = await LazyAssemblyLoader
        .LoadAssembliesAsync(["MyHeavyFeature.wasm"]);
    

JS Interop

Calling JavaScript from C#
@inject IJSRuntime JS

await JS.InvokeVoidAsync("alert", "Hello from Blazor!");
var result = await JS.InvokeAsync<string>("prompt", "Enter name:");
Calling C# from JavaScript
[JSInvokable]
public static string GetMessage() => "Hello from C#!";
DotNet.invokeMethodAsync('MyAssembly', 'GetMessage')
    .then(result => console.log(result));

Deliver

  • interactive Blazor components with appropriate render mode
  • efficient state management and data flow
  • proper handling of prerendering scenarios
  • performant list rendering with virtualization

Validate

  • components render correctly in chosen mode
  • state persists correctly across prerender/hydration
  • no unnecessary re-renders (check with browser tools)
  • JS interop works in both Server and WASM
  • error boundaries catch component failures
  • Auto mode works in both environments

Comments

Markdown supported