Skill v1.0.1
currentAutomated scan100/100+5 new
version: "1.0.1" name: blazor description: "Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices. USE FOR: building interactive web UIs with C# instead of JavaScript; choosing between Server, WebAssembly, or Auto render modes; designing component hierarchies and state. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made." compatibility: "Requires Blazor project (.NET 6+, preferably .NET 8+ for unified model)."
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+)
| Mode | Where It Runs | Best For | |
|---|---|---|---|
Static | Server (no interactivity) | SEO pages, marketing content | |
InteractiveServer | Server via SignalR | Real-time apps, thin clients | |
InteractiveWebAssembly | Browser via WASM | Offline-capable, client-heavy | |
InteractiveAuto | Server first, then WASM | Best 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 responseSubsequent Requests:Browser → WASM (downloaded in background) → No server needed
Workflow
- 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
- Design components for reusability:
- Small, focused components
- Parameters for customization
- Events for communication
- Handle state correctly:
- Component state lives in component
- Shared state via services (DI)
- Persist state across prerender with
[PersistentState]
- Validate in both environments (for Auto mode)
Current Upstream Notes
- Treat
dotnet/aspnetcorev10.0.11as 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 prerenderProducts ??= await Http.GetFromJsonAsync<List<Product>>("api/products");}}
Data Access Pattern for Auto Mode
// Shared interfacepublic 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-Pattern | Why It's Bad | Better Approach | |
|---|---|---|---|
| Large components | Hard to maintain, slow renders | Split into smaller components | |
| Direct DB access in WASM | No DB in browser | Use HTTP API | |
Ignoring ShouldRender | Unnecessary re-renders | Override when needed | |
| Sync JS interop in Server | Blocks SignalR circuit | Use IJSRuntime async | |
| No error boundaries | One error crashes app | Use <ErrorBoundary> | |
| Forgetting prerender state | Double API calls | Use [PersistentState] |
Performance Best Practices
- Virtualize large lists:
``razor <Virtualize Items="@products" Context="product"> <ProductCard Product="@product" /> </Virtualize> ``
- Use `@key` for list diffing:
``razor @foreach (var item in items) { <ItemComponent @key="item.Id" Item="@item" /> } ``
- Debounce rapid events:
```csharp private Timer? _debounceTimer;
private void OnInput(ChangeEventArgs e) { _debounceTimer?.Dispose(); _debounceTimer = new Timer(_ => InvokeAsync(DoSearch), null, 300, Timeout.Infinite); } ```
- Lazy load assemblies (WASM):
``csharp var assemblies = await LazyAssemblyLoader .LoadAssembliesAsync(["MyHeavyFeature.wasm"]); ``
JS Interop
Calling JavaScript from C#
@inject IJSRuntime JSawait 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