- name
- aspnetcore-blazor10
- description
- Provides up-to-date information about ASP.NET Core 10 and Blazor .NET 10 new features. Use when the user asks about Blazor .NET 10 capabilities, ASP.NET Core 10 features, OpenAPI, Minimal APIs, SignalR, or needs guidance on ASP.NET Core/Blazor .NET 10 specific features.
# ASP.NET Core 10 / Blazor .NET 10 New Features Reference
Source: https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-10.0
---
## Blazor
### Blazor Component Coding Conventions
以下のルールに従ってコンポーネントを実装すること。
#### コードビハインド分離(`@code` ブロック禁止)
`.razor` ファイルに `@code {}` ブロックを記述せず、C# コードは `.razor.cs` ファイルに分離する。コードビハインドクラスは `partial class` として定義する。
```razor
@* Counter.razor — マークアップのみ *@
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>
```
```csharp
// Counter.razor.cs
public partial class Counter
{
private int currentCount;
private void IncrementCount()
{
currentCount++;
}
}
```
#### DI はコンストラクターインジェクションを使用
`@inject` ディレクティブや `[Inject]` 属性ではなく、コンストラクターインジェクションを使用する。.NET 10 の Blazor コンポーネントではコンストラクターインジェクションがサポートされている。
```csharp
// MyComponent.razor.cs
public partial class MyComponent(IMyService myService, NavigationManager navigationManager)
{
protected override async Task OnInitializedAsync()
{
var data = await myService.GetDataAsync();
}
}
```
#### コンポーネント固有の CSS は `.razor.css` に分離
コンポーネントに固有のスタイルはコンポーネントと同名の `.razor.css` ファイルに定義する(CSS isolation)。
```css
/* Counter.razor.css */
h1 {
color: #333;
}
button {
background-color: #0078d4;
color: white;
}
```
---
### QuickGrid Enhancements
**`RowClass` parameter** — Apply a CSS class to a row based on row item:
```razor
<QuickGrid Items="movies" RowClass="GetRowCssClass">
<PropertyColumn Property="@(m => m.Title)" />
</QuickGrid>
@code {
private string? GetRowCssClass(Movie item) =>
item.IsArchived ? "row-archived" : null;
}
```
**`HideColumnOptionsAsync`** — Close column options UI programmatically:
```razor
<QuickGrid @ref="grid" Items="items">
<PropertyColumn Property="@(m => m.Title)">
<ColumnOptions>
<input @bind="filter" @bind:after="@(() => grid.HideColumnOptionsAsync())" />
</ColumnOptions>
</PropertyColumn>
</QuickGrid>
```
---
### `[PersistentState]` — Declarative Prerendering State Persistence
Before (.NET 9):
```razor
@implements IDisposable
@inject PersistentComponentState ApplicationState
@code {
public List<Movie>? MoviesList { get; set; }
private PersistingComponentStateSubscription? sub;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList), out var movies))
MoviesList = await MovieService.GetMoviesAsync();
else
MoviesList = movies;
sub = ApplicationState.RegisterOnPersisting(() =>
{
ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
return Task.CompletedTask;
});
}
public void Dispose() => sub?.Dispose();
}
```
After (.NET 10):
```razor
@code {
[PersistentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
```
**`AllowUpdates`** — opt-in to update state during enhanced navigation:
```csharp
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
```
**`RestoreBehavior`** options:
```csharp
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
```
**Custom serializer for persistent state:**
```csharp
// Program.cs
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>, CustomUserSerializer>();
```
---
### Improved Form Validation
Validates nested objects and collection items. Uses source generator (AOT-compatible) instead of reflection.
**Setup:**
```csharp
// Program.cs
builder.Services.AddValidation();
```
**Model** (must be a `.cs` file, not inline in `.razor`):
```csharp
[ValidatableType] // required on root type only
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}
public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }
[Required(ErrorMessage = "Email is required.")]
[EmailAddress]
public string? Email { get; set; }
}
```
**Component** — uses `DataAnnotationsValidator` as before:
```razor
<EditForm Model="Model">
<DataAnnotationsValidator />
<InputText @bind-Value="Model!.Customer.FullName" />
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</EditForm>
```
**Validation rules** (all applied in order; short-circuits on first error):
1. Member property attributes (including nested objects)
2. Type-level attributes
3. `IValidatableObject.Validate`
`[SkipValidation]` to exclude specific properties or types from validation.
---
### New JavaScript Interop APIs
**Create a JS object instance:**
```csharp
// Async
var objRef = await JSRuntime.InvokeConstructorAsync("MyLib.MyClass", arg1, arg2);
var value = await objRef.GetValueAsync<string>("propertyName");
await objRef.SetValueAsync("propertyName", newValue);
// Sync (in-process)
var inProc = (IJSInProcessRuntime)JSRuntime;
var objRef = inProc.InvokeConstructor("MyLib.MyClass", arg1);
var value = objRef.GetValue<string>("propertyName");
inProc.SetValue("propertyName", newValue);
```
**Read/write JS object properties:**
```csharp
// Read
var num = await JSRuntime.GetValueAsync<int>("myLib.config.retryCount");
// Write
await JSRuntime.SetValueAsync("myLib.config.retryCount", 5);
```
---
### Not Found Handling
**`Router.NotFoundPage` parameter** — specify a page for 404:
```razor
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<!-- <NotFound> is ignored when NotFoundPage is defined -->
</Router>
```
**`NavigationManager.NotFound()`** — trigger Not Found programmatically:
```csharp
@inject NavigationManager Nav
protected override async Task OnInitializedAsync()
{
var item = await Service.GetItemAsync(Id);
if (item is null)
Nav.NotFound();
}
```
Behavior:
- Static SSR: sets HTTP 404
- Interactive rendering: renders the `NotFoundPage`
- Streaming rendering: renders `NotFoundPage` via enhanced navigation
Subscribe to notifications: `NavigationManager.OnNotFound`.
---
### Passkey (WebAuthn/FIDO2) Support for ASP.NET Core Identity
Password-less login using biometrics or security keys. Built into the Blazor Web App project template.
```csharp
// Program.cs — already included in the new project template
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
```
See: https://learn.microsoft.com/aspnet/core/security/authentication/passkeys
---
### Circuit State Persistence
Server-side Blazor (SignalR) sessions survive connection loss without losing unsaved work:
- Browser tab throttling
- Mobile app switching
- Network interruptions
- Proactive pausing of inactive circuits
- Enhanced navigation
See: https://learn.microsoft.com/aspnet/core/blazor/state-management/server
---
### Hot Reload for Blazor WebAssembly
```xml
<!-- Enabled by default for Debug configuration -->
<WasmEnableHotReload>true</WasmEnableHotReload>
```
---
### Navigation Changes
- **`NavigateTo` same-page**: No longer scrolls to top (viewport preserved on query string / fragment changes)
- **`NavLink` with `NavLinkMatch.All`**: Ignores query string and fragment — link stays `active` on path match
Revert: set `AppContext` switch `Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragment` to `true`
Override matching:
```csharp
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute) { ... }
}
```
---
### Disable `NavigationException` during Static SSR
```xml
<!-- Program.cs template sets this to true by default in .NET 10 -->
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
```
With this set, `NavigationManager.NavigateTo` during static SSR no longer throws `NavigationException`. Code after `NavigateTo` executes before the redirect occurs.
---
### Reconnection UI (`ReconnectModal`)
Template now includes a `ReconnectModal` component with collocated CSS/JS. CSP-compliant (no inline styles).
New event: `components-reconnect-state-changed`
New state: `"retrying"` (in addition to existing states)
---
### `OwningComponentBase` implements `IAsyncDisposable`
```csharp
// DisposeAsync and DisposeAsyncCore now available
public class MyComponent : OwningComponentBase
{
// async cleanup supported
}
```
---
### New `InputHidden` Component
```razor
<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="Example">
<InputHidden id="hidden" @bind-Value="Parameter" />
<button type="submit">Submit</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public string Parameter { get; set; } = "stranger";
private void Submit() { }
}
```
---
### Client-Side Fingerprinting (Standalone Blazor WebAssembly)
```xml
<!-- .csproj -->
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
```
```html
<!-- wwwroot/index.html -->
<head>
<script type="importmap"></script>
</head>
<body>
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
```
Fingerprint JS modules:
```html
<script type="module" src="js/scripts#[.{fingerprint}].js"></script>
```
```xml
<ItemGroup>
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js" Expression="#[.{fingerprint}]!" />
</ItemGroup>
```
---
### WebAssembly Environment Configuration
```xml
<!-- No longer uses launchSettings.json -->
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
```
Defaults: `Development` (build), `Production` (publish).
---
### `HttpClient` Response Streaming Enabled by Default
`response.Content.ReadAsStreamAsync()` now returns `BrowserHttpReadStream` (not `MemoryStream`). Synchronous `Stream.Read` is not supported.
Opt out globally:
```xml
<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
```
Opt out per request:
```csharp
requestMessage.SetBrowserResponseStreamingEnabled(false);
```
---
### Static Asset Preloading
**Blazor Web Apps** — use `<ResourcePreloader />` component:
```razor
<!-- App.razor -->
<head>
<base href="/" />
<ResourcePreloader />
</head>
```
**Standalone Blazor WebAssembly** — add `<link rel="preload" id="webassembly" />` in `wwwroot/index.html` (requires `OverrideHtmlAssetPlaceholders`).
---
### Blazor Script as Static Web Asset
Auf GitHub ansehen