agentleFS
Postpartum-genushyacinthus29Postpartum-genushyacinthus29/dotnet-skills/skills/dotnet-blazor/SKILL.md

dotnet-blazor

Skill13 starsChanged 8 months agoMITView on GitHub

Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices.

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    no unnecessary re-renders (check with browser tools)

    line 244

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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

Documentation

References

  • patterns.md - Detailed component patterns, state management strategies, and JS interop techniques
  • anti-patterns.md - Common Blazor mistakes and how to avoid them

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)

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

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.