Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Hosting model selection and render modes -- see [skill:dotnet-blazor-patterns]
Auth components (AuthorizeView, CascadingAuthenticationState) -- see [skill:dotnet-blazor-auth]
bUnit testing -- see [skill:dotnet-blazor-testing]
Standalone SignalR hub patterns -- see [skill:dotnet-realtime-communication]
E2E testing -- see [skill:dotnet-playwright]
UI framework selection -- see [skill:dotnet-ui-chooser]
Accessibility patterns (ARIA, keyboard navigation) -- see [skill:dotnet-accessibility]
Cross-references: [skill:dotnet-blazor-patterns] for hosting models and render modes, [skill:dotnet-blazor-auth] for
authentication, [skill:dotnet-blazor-testing] for bUnit testing, [skill:dotnet-realtime-communication] for standalone
SignalR, [skill:dotnet-playwright] for E2E testing, [skill:dotnet-ui-chooser] for framework selection,
[skill:dotnet-accessibility] for accessibility patterns (ARIA, keyboard nav, screen readers).
Component Lifecycle
Lifecycle Methods
@code {
// 1. Called when parameters are set/updatedpublicoverrideasync Task SetParametersAsync(ParameterView parameters)
{
// Access raw parameters before they are appliedawaitbase.SetParametersAsync(parameters);
}
// 2. Called after parameters are assigned (sync)protectedoverridevoidOnInitialized()
{
}
{
products = ProductService.GetProductsAsync();
}
{
}
{
(firstRender)
{
}
}
{
(firstRender)
{
JSRuntime.InvokeVoidAsync(, chartElement);
}
}
{
}
{
(module )
{
module.DisposeAsync();
}
}
}
```text
| Lifecycle Event | Static SSR | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| ---------------------- | --------------------- | -------------------------------------------- | -------------------------------- | ------------------------------------------------------------------ | --------------------------- |
| `OnInitialized(Async)` | Runs server | Runs server | Runs browser | Server first load, browser after WASM cached | Runs -process |
| `OnAfterRender(Async)` | Never called | Runs server after SignalR confirms render | Runs browser after DOM update | Server-side then browser-side (matches active runtime) | Runs after WebView render |
| `Dispose(Async)` | Called after response | Called circuit ends | Called component removal | = Name=>
<Router AppAssembly=>
<!-- All descendants can receive AppTheme -->
</Router>
</CascadingValue>
@code {
ThemeSettings theme = () { IsDarkMode = , AccentColor = };
}
```text
```razor
<!-- Child: consume the cascading -->
@code {
[]
ThemeSettings? Theme { ; ; }
}
```text
**=` to avoid
re-render overhead:
```razor
<CascadingValue Value= IsFixed=>
<ChildComponent />
</CascadingValue>
```text
```csharp
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddSingleton<AppState>();
@inject IProductService ProductService
@inject AppState State
```text
**DI lifetime behavior per render mode:**
| Lifetime | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| --------- | ---------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- |
| Singleton | Shared across all circuits the server | One per browser tab | Server-shared during Server phase; per-tab after WASM | One per app instance |
| Scoped |
{
(firstRender)
{
SessionStorage.SetAsync(, cartItems);
result = SessionStorage.GetAsync<List<CartItem>>();
(result.Success) { cartItems = result.Value!; }
LocalStorage.SetAsync(, userPrefs);
}
}
```text
For InteractiveWebAssembly, use JS interop to access browser storage directly:
```csharp
JSRuntime.InvokeVoidAsync(, ,
JsonSerializer.Serialize(, AppJsonContext.Default.UserPrefs));
json = JSRuntime.InvokeAsync<?>(, );
(json )
{
= JsonSerializer.Deserialize(json, AppJsonContext.Default.UserPrefs);
}
```json
**Gotcha:** `ProtectedBrowserStorage` available during prerendering. Always access it
`OnAfterRenderAsync(firstRender: )`, never `OnInitializedAsync`.
---
```csharp
@inject IJSRuntime JSRuntime
JSRuntime.InvokeVoidAsync(, );
width = JSRuntime.InvokeAsync<>();
result = JSRuntime.InvokeAsync<>(
,
TimeSpan.FromSeconds(),
inputData);
```text
```csharp
IJSObjectReference? module;
{
(firstRender)
{
module = JSRuntime.InvokeAsync<IJSObjectReference>(
, );
module.InvokeVoidAsync(, elementRef);
}
}
{
(module )
{
module.DisposeAsync();
}
}
```text
```javascript
{
}
{
element.;
}
```text
```csharp
DotNetObjectReference<MyComponent>? dotNetRef;
{
dotNetRef = DotNetObjectReference.Create();
}
[]
{
message = data;
StateHasChanged();
}
{
dotNetRef?.Dispose();
}
```text
```javascript
{
document.addEventListener(, e => {
dotNetRef.invokeMethodAsync(, e.detail);
});
}
```text
| Concern | InteractiveServer | InteractiveWebAssembly | InteractiveAuto | Hybrid |
| ------------------------- | ----------------------------- | ------------------------------- | ----------------------------------------------------------------------- | ------------------------------- |
| JS call timing | After SignalR confirms render | After WASM runtime loads | SignalR initially, then direct after WASM | After WebView loads |
| `OnAfterRender` available | Yes | Yes | Yes | Yes |
| IJSRuntime sync calls | ; `IJSInProcessRuntime` after WASM | `IJSInProcessRuntime` available |
| Module imports | = OnValidSubmit= FormName=>
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label =>Name:</label>
<InputText id= @bind-Value= />
<ValidationMessage For= />
</div>
<div>
<label =>Price:</label>
<InputNumber id= @bind-Value= />
<ValidationMessage For= />
</div>
<div>
<label =>Category:</label>
<InputSelect id= @bind-Value=>
<option =>Select...</option>
<option =>Electronics</option>
<option =>Clothing</option>
</InputSelect>
<ValidationMessage For= />
</div>
<button type=>Save</button>
</EditForm>
@code {
ProductModel product = ();
{
ProductService.CreateAsync(product);
Navigation.NavigateTo();
}
}
```text
```csharp
{
[]
[]
Name { ; ; } = ;
[]
Price { ; ; }
[]
Category { ; ; } = ;
}
```text
Static SSR forms require `FormName` use `[SupplyParameterFromForm]`:
```razor
@page
<EditForm Model= OnValidSubmit= FormName= Enhance>
<DataAnnotationsValidator />
<!-- form fields -->
<button type=>Create</button>
</EditForm>
@code {
[]
ProductModel product { ; ; } = ();
{
ProductService.CreateAsync(product);
Navigation.NavigateTo();
}
}
```text
The `Enhance` attribute enables enhanced form handling -- the form submits via fetch patches the DOM without a full
page reload.
**Gotcha:** `FormName` must be unique across all forms the page. Duplicate `FormName` values cause ambiguous form
submission errors.
---
QuickGrid a high-=>
<PropertyColumn Property= Sortable= />
<PropertyColumn Property= Format= Sortable= />
<PropertyColumn Property= Sortable= />
<TemplateColumn Title=>
<button @onclick=>Edit</button>
</TemplateColumn>
</QuickGrid>
@code {
IQueryable<Product> products = Enumerable.Empty<Product>().AsQueryable();
{
list = ProductService.GetAllAsync();
products = list.AsQueryable();
}
=> Navigation.NavigateTo();
}
```text
```razor
<QuickGrid Items= Pagination=>
<PropertyColumn Property= Sortable= />
<PropertyColumn Property= Format= />
</QuickGrid>
<Paginator State= />
@code {
PaginationState pagination = () { ItemsPerPage = };
IQueryable<Product> products = !;
}
```text
For large datasets, virtualization renders only visible rows:
```razor
<QuickGrid Items= Virtualize= ItemSize=>
<PropertyColumn Property= />
<PropertyColumn Property= Format= />
</QuickGrid>
```text
<!-- net11-preview -->
.NET adds `OnRowClick` to QuickGrid row-level click handling without template columns:
```razor
<QuickGrid Items= OnRowClick=>
<PropertyColumn Property= />
<PropertyColumn Property= Format= />
</QuickGrid>
@code {
{
Navigation.NavigateTo();
}
}
```text
**Fallback (net10):** Use a `TemplateColumn` a click handler wrap each row a clickable element.
Source:
[](https:
---
<!-- net11-preview -->
`EnvironmentBoundary` =>
<p>Debug panel -- only visible Development</p>
<DebugToolbar />
</EnvironmentBoundary>
<EnvironmentBoundary Exclude=>
<p>Testing controls -- hidden Production</p>
</EnvironmentBoundary>
```text
**Fallback (net10):** Inject `IWebHostEnvironment` use conditional rendering `@code`.
Source:
[](https:
.NET adds `[DisplayName]` support input components, automatically generating `<label>` elements:
```razor
<EditForm Model= FormName=>
<!-- Automatically renders <label> [DisplayName] -->
<InputText @bind-Value= />
<InputText @bind-Value= />
</EditForm>
@code {
ContactModel model = ();
}
{
[]
[]
FullName { ; ; } = ;
[]
[]
EmailAddress { ; ; } = ;
}
```text
**Fallback (net10):** Add `<label =>` elements manually.
Source:
[](https:
.NET allows `IHostedService` implementations to run Blazor WebAssembly, enabling background tasks the browser:
```csharp
builder.Services.AddHostedService<DataSyncService>();
:
{
{
(!stoppingToken.IsCancellationRequested)
{
SyncDataFromServer();
Task.Delay(TimeSpan.FromMinutes(), stoppingToken);
}
}
}
```text
**Fallback (net10):** Use a `Timer` a component inject a singleton service that starts background work first
use.
Source:
[](https:
<!-- net11-preview -->
.NET adds `ConfigureConnection` to the Blazor Server circuit hub, ;
```text
**Fallback (net10):** Use `IHubFilter` middleware to inspect/modify connections at the hub level.
Source:
[](https:
---
**Do call JS interop `OnInitializedAsync`.** The DOM available yet. Use
`OnAfterRenderAsync(firstRender: )` JS calls that need DOM elements.
**Do forget `StateHasChanged()` after external state changes.** When state changes a non-
// One-time initialization (runs once per component instance)
// 3. Called after parameters are assigned (async)
protectedoverrideasync Task OnInitializedAsync()
// Async initialization (data fetching, service calls)
Called when circuit ends (Server phase) oronremoval (WASM phase) | Called on component removal |
**Gotcha:** In Static SSR, `OnAfterRender` never executes because there is no persistent connection. Do not place
critical logic in `OnAfterRender` for Static SSR pages.
---
## State Management
### Cascading Values
Cascading values flow data down the component tree without explicit parameter passing.
```razor
<!-- Parent: provide a cascading value -->
<CascadingValue Value
"@theme"
"AppTheme"
"typeof(App).Assembly"
private
new
false
"#0078d4"
value
CascadingParameter(Name = "AppTheme")
public
get
set
Fixed cascading values (.NET 8+):** For values that never change after initial render, use `IsFixed
"true"
"@config"
"true"
### Dependency Injection
// Register services in Program.cs
// Inject in components
on
switch
One per circuit (acts like per-user) | One per browser tab (same as Singleton) | Per-circuit (Server phase), per-tab (WASM phase) -- state does not transfer between phases | One per app instance (same as Singleton) |
| Transient | New instance each injection | New instance each injection | New instance each injection | New instance each injection |
**Gotcha:** In Blazor Server, `Scoped` services live for the entire circuit duration (not per-request like in MVC). A
circuit persists until the user navigates away or the connection drops. Long-lived scoped services may accumulate state
-- use `OwningComponentBase<T>` for component-scoped DI.
### Browser Storage
```csharp
// ProtectedBrowserStorage -- encrypted, per-user storage// Available in InteractiveServer only (not WASM -- server encrypts/decrypts)
@inject ProtectedSessionStorage SessionStorage
@inject ProtectedLocalStorage LocalStorage
protectedoverrideasync Task OnAfterRenderAsync(bool firstRender)
if
// Session storage (cleared when tab closes)
await
"cart"
var
await
"cart"
if
// Local storage (persists across sessions)
await
"preferences"
// WASM: Direct browser storage via JS interop
await
"localStorage.setItem"
"key"
value
var
await
string
"localStorage.getItem"
"key"
if
is
not
null
value
is
not
in
true
in
## JavaScript Interop
### Calling JavaScript from .NET
// Invoke a global JS function
await
"console.log"
"Hello from Blazor"
// Invoke and get a return value
var
await
int
"getWindowWidth"
// With timeout (important for Server to avoid hanging circuits)
Not supported (async only) | `IJSInProcessRuntime` available | Async-only during Server phase
switch
Via SignalR (latency) | Direct (fast) | SignalR (Server phase), direct (WASM phase) | Direct (fast) |
**Gotcha:** In InteractiveServer, all JS interop calls travel over SignalR, adding network latency. Minimize round trips
by batching operations into a single JS function call.
---
## EditForm Validation
### Basic EditForm with Data Annotations
```razor
<EditForm Model
"product"
"HandleSubmit"
"product-form"
for
"name"
"name"
"product.Name"
"() => product.Name"
for
"price"
"price"
"product.Price"
"() => product.Price"
for
"category"
"category"
"product.Category"
value
""
value
"Electronics"
value
"Clothing"
"() => product.Category"
"submit"
private
new
privateasync Task HandleSubmit()
await
"/products"
### Model with Validation Attributes
public
sealed
class
ProductModel
Required(ErrorMessage = "Product name is required")
StringLength(200, MinimumLength = 1)
public
string
get
set
""
Range(0.01, 1_000_000, ErrorMessage = "Price must be between {1} and {2}")
public
decimal
get
set
Required(ErrorMessage = "Category is required")
public
string
get
set
""
### EditForm with Enhanced Form Handling (.NET 8+)
and
"/products/create"
"product"
"HandleSubmit"
"create-product"
"submit"
SupplyParameterFromForm
private
get
set
new
privateasync Task HandleSubmit()
await
"/products"
and
on
## QuickGrid
is
performance grid component built intoBlazor (.NET 8+). It supports sorting, filtering, pagination,
and virtualization.
### Basic QuickGrid
```razor
@using Microsoft.AspNetCore.Components.QuickGrid
<QuickGrid Items
Blazor context
(timer, event handler, JS callback), call `StateHasChanged()` or `InvokeAsync(StateHasChanged)` to trigger re-render.
3. **Do not use `ProtectedBrowserStorage` during prerendering.** It throws because no interactive circuit exists yet.
Access it only in `OnAfterRenderAsync`.
4. **Do not forget `FormName` on Static SSR forms.** Without it, form submissions in Static SSR mode are not routed to
the correct handler.
5. **Do not dispose `DotNetObjectReference` before JS is done with it.** Premature disposal causes `JSException` when
JavaScript tries to invoke the callback. Dispose in `Dispose()` or `DisposeAsync()`.
6. **Do not assume Scoped services are per-request in Blazor Server.** Scoped services live for the entire circuit. Use
`OwningComponentBase<T>` when you need component-scoped service lifetimes.
---
## Prerequisites
- .NET 8.0+ (QuickGrid, enhanced form handling, cascading values with `IsFixed`)
- `Microsoft.AspNetCore.Components.QuickGrid` package for QuickGrid
- .NET 11 preview for EnvironmentBoundary, Label/DisplayName, QuickGrid OnRowClick, IHostedService in WASM
---
## Knowledge Sources
Blazor component patterns inthis skill are grounded in guidance from:
- **Damian Edwards** -- Razor and Blazor component design patterns, render mode architecture, and performance best
practices. Principal architect on the ASP.NET team.
> These sources inform the patterns and rationale presented above. This skill does not claim to represent or speak for
> any individual.
---
## Code Navigation (Serena MCP)
**Primary approach:** Use Serena symbol operations for efficient code navigation:
1. **Find definitions**: `serena_find_symbol` instead of text search
2. **Understand structure**: `serena_get_symbols_overview` forfile organization
3. **Track references**: `serena_find_referencing_symbols` for impact analysis
4. **Precise edits**: `serena_replace_symbol_body` for clean modifications
**When to use Serena vs traditional tools:**
- ✅ **Use Serena**: Navigation, refactoring, dependency analysis, precise edits
- ✅ **Use Read/Grep**: Reading full files, pattern matching, simple text operations
- ✅ **Fallback**: If Serena unavailable, traditional tools work fine
**Example workflow:**
```text
# Instead of:
Read: src/Services/OrderService.cs
Grep: "publicvoid ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"
```
## References
- [Blazor Component Lifecycle](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-10.0)
- [Blazor State Management](https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-10.0)
- [Blazor JS Interop](https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-10.0)
- [Blazor Forms and Validation](https://learn.microsoft.com/en-us/aspnet/core/blazor/forms/?view=aspnetcore-10.0)
- [QuickGrid Component](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/quickgrid?view=aspnetcore-10.0)
- [Cascading Values and Parameters](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-10.0)