home / skills / stuartf303 / sorcha / blazor

blazor skill

/.claude/skills/blazor

This skill helps you build and configure Blazor WASM components with MudBlazor across server and client render modes for admin UIs.

npx playbooks add skill stuartf303/sorcha --skill blazor

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
4.7 KB
---
name: blazor
description: |
  Builds Blazor WASM components for admin and main UI applications.
  Use when: Creating/modifying Razor components, configuring render modes, implementing authentication, managing component state, or working with MudBlazor components.
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, mcp__context7__resolve-library-id, mcp__context7__query-docs
---

# Blazor Skill

Sorcha uses Blazor with hybrid rendering (Server + WebAssembly). The Admin UI (`src/Apps/Sorcha.Admin/`) runs behind YARP API Gateway. Components use MudBlazor for UI and support three render modes: static server, interactive server, and interactive WASM.

## Quick Start

### Render Mode Selection

```razor
@* WASM - Complex interactive pages (Designer, Diagrams) *@
@page "/designer"
@rendermode InteractiveWebAssembly
@attribute [Authorize]

@* Server - Admin pages needing real-time SignalR *@
@page "/admin/audit"
@rendermode @(new InteractiveServerRenderMode(prerender: false))
@attribute [Authorize(Roles = "Administrator")]

@* Static - Public pages (Login) - no @rendermode directive *@
@page "/login"
@attribute [AllowAnonymous]
```

### Component with Loading State

```razor
@inject HttpClient Http

<MudPaper Elevation="2" Class="pa-4">
    @if (_isLoading && !_hasLoadedOnce)
    {
        <MudProgressCircular Indeterminate="true" Size="Size.Small" />
    }
    else if (_data != null)
    {
        <MudText>@_data.Title</MudText>
    }
    else if (_errorMessage != null)
    {
        <MudAlert Severity="Severity.Error">@_errorMessage</MudAlert>
    }
</MudPaper>

@code {
    private DataDto? _data;
    private string? _errorMessage;
    private bool _isLoading;
    private bool _hasLoadedOnce;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
            await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        _isLoading = true;
        try
        {
            _data = await Http.GetFromJsonAsync<DataDto>("/api/data");
            _hasLoadedOnce = true;
        }
        catch (Exception ex)
        {
            _errorMessage = ex.Message;
        }
        finally
        {
            _isLoading = false;
            StateHasChanged();
        }
    }
}
```

## Key Concepts

| Concept | Usage | Example |
|---------|-------|---------|
| Render Mode | Control where component runs | `@rendermode InteractiveWebAssembly` |
| CascadingParameter | Receive parent state | `[CascadingParameter] MudBlazor.IDialogReference? MudDialog` |
| OnAfterRenderAsync | Initialize after DOM ready | `if (firstRender) await LoadAsync();` |
| StateHasChanged | Trigger re-render | Call after async state updates |
| NavigationManager | Programmatic navigation | `Navigation.NavigateTo("/", forceLoad: true)` |

## Project Structure

| Project | Purpose | Render Mode |
|---------|---------|-------------|
| `Sorcha.Admin` | Server host, auth, API proxy | Server + prerender |
| `Sorcha.Admin.Client` | WASM components | WebAssembly |
| `Sorcha.UI.Core` | Shared components | Both |
| `Sorcha.UI.Web` | Main UI server | Server |
| `Sorcha.UI.Web.Client` | Main UI WASM | WebAssembly |

## Common Patterns

### MudBlazor Dialog

```razor
<MudDialog DisableSidePadding="false">
    <DialogContent>
        <MudTextField @bind-Value="_value" Label="Input" />
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="Cancel">Cancel</MudButton>
        <MudButton Color="Color.Primary" OnClick="Submit">OK</MudButton>
    </DialogActions>
</MudDialog>

@code {
    [CascadingParameter] MudBlazor.IDialogReference? MudDialog { get; set; }
    private string _value = "";
    
    private void Cancel() => MudDialog?.Close();
    private void Submit() => MudDialog?.Close(DialogResult.Ok(_value));
}
```

### Opening Dialog from Parent

```csharp
var dialog = await DialogService.ShowAsync<LoginDialog>("Login");
var result = await dialog.Result;
if (result is { Canceled: false })
{
    // Handle success
}
```

## See Also

- [patterns](references/patterns.md) - Component and authentication patterns
- [workflows](references/workflows.md) - Development and deployment workflows

## Related Skills

- See the **aspire** skill for service discovery configuration
- See the **signalr** skill for real-time notifications
- See the **jwt** skill for authentication token handling
- See the **yarp** skill for API Gateway configuration
- See the **mudblazor** skill for component library details

## Documentation Resources

> Fetch latest Blazor/MudBlazor documentation with Context7.

**Library ID:** `/websites/mudblazor` _(MudBlazor component library documentation)_

**Recommended Queries:**
- "MudBlazor dialog service usage"
- "MudBlazor form validation"
- "MudBlazor data grid filtering"

Overview

This skill builds Blazor WebAssembly and hybrid Server+WASM components for admin and main UI applications. It focuses on Razor component patterns, render mode configuration, MudBlazor component usage, and common lifecycle/state patterns. Use it to implement interactive pages, dialogs, authentication-aware pages, and consistent loading/error handling.

How this skill works

The skill inspects and generates Razor components and supporting C# code that follow hybrid render modes: static server, interactive server (SignalR), and interactive WebAssembly. It scaffolds patterns for loading state, dialogs using MudBlazor, cascading parameters, navigation, and authentication attributes. It also recommends project placement between server hosts and WASM client projects.

When to use it

  • Creating or modifying Razor components for admin or main UI
  • Switching or configuring component render modes (Static, Interactive Server, WASM)
  • Implementing authentication/authorization on pages and components
  • Managing async component state and lifecycle (OnAfterRenderAsync, StateHasChanged)
  • Building dialogs and interactions with MudBlazor (DialogService, MudDialog)

Best practices

  • Initialize data in OnAfterRenderAsync when DOM must be ready; guard with firstRender to avoid repeated loads.
  • Keep loading/error UI consistent: show spinner first, then content or a readable error alert, and set flags like _hasLoadedOnce.
  • Use CascadingParameter for dialog references and shared state; call StateHasChanged after async updates.
  • Use NavigationManager for programmatic navigation and forceLoad when doing full reloads across hosts.

Example use cases

  • Create a Designer page with @rendermode InteractiveWebAssembly for complex client-side interactions and diagrams.
  • Build an Admin audit page with InteractiveServer render mode to use SignalR for live updates and role-based [Authorize(Roles = "Administrator")].
  • Implement a login page without @rendermode for static prerendered public access and [AllowAnonymous] attribute.
  • Add a MudBlazor dialog for inline forms using CascadingParameter MudDialog and DialogService.ShowAsync from the parent component.
  • Implement consistent data loading pattern: spinner -> content -> error with HttpClient.GetFromJsonAsync calls and StateHasChanged.

FAQ

When should I use prerendering with InteractiveServer render mode?

Use prerendering when you want faster initial paint from the server but still need interactive SignalR features after the client connects. Set prerender to false if you must avoid executing client-only code during prerender.

How do I handle long-running async loads without blocking the UI?

Mark the component as loading, start the async call in OnAfterRenderAsync on firstRender, and update _isLoading/_hasLoadedOnce. Always call StateHasChanged after updates to force a re-render.