.NET 10 shipped with significant Blazor improvements. Smaller bundle sizes, persistent circuit state, passkey authentication, and better 404 handling. Here's what matters.
// .NET 10 Blazor — Persistent state is now simple
@code {
[PersistentState]
public string UserPreference { get; set; }
}
One attribute. State survives reconnections.
How Much Smaller Is the Blazor Bundle?
The core blazor.web.js script dropped from 183 KB to 43 KB — a 76% reduction.
How? The script is now served as a static asset with automatic compression and fingerprinting. Faster initial loads, better caching.
No code changes needed. Upgrade to .NET 10 and the improvement is automatic.
What Is Persistent State?
Blazor Server can now persist circuit state across reconnections:
@page "/preferences"
@code {
[PersistentState]
public UserSettings Settings { get; set; } = new();
private void SaveTheme(string theme)
{
Settings.Theme = theme;
}
}
If a user disconnects and reconnects — or if the server evicts the circuit — state survives. Previously, users lost all state on reconnection.
Control circuit behavior programmatically:
// JavaScript control
Blazor.pause(); // Pause circuit
Blazor.resume(); // Resume circuit
How Does Passkey Authentication Work?
ASP.NET Core Identity now supports WebAuthn/FIDO2 passkeys:
// Program.cs
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddPasskeys(); // New in .NET 10
Users can sign in without passwords using biometrics or hardware keys. Phishing-resistant by design.
What's New in Form Validation?
Nested form validation is now supported:
// Program.cs
builder.Services.AddValidatableTypes();
// Models
[ValidatableType]
public class Order
{
[Required]
public string CustomerName { get; set; }
[ValidateComplexType]
public Address ShippingAddress { get; set; }
}
public class Address
{
[Required]
public string Street { get; set; }
[Required]
public string City { get; set; }
}
Register the validation system, add [ValidatableType] to root types, and nested validation works automatically.
How Do I Handle 404 Pages?
.NET 10 introduces automatic 404 handling:
// In any component
NavigationManager.NotFound();
This works in both static SSR and interactive render modes. New project templates include a default NotFound.razor page.
@* NotFound.razor — included in new templates *@
@page "/not-found"
<h1>Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="/">Return Home</a>
No more manual 404 handling boilerplate.
What's New in JS Interop?
JavaScript interop gained new capabilities:
// Create JS object instance
var instance = await JS.InvokeAsync<IJSObjectReference>(
"createInstance", "MyClass", arg1, arg2);
// Read JS property
var value = await JS.GetValueAsync<string>("window.myObject.property");
// Set JS property
await JS.SetValueAsync("window.myObject.property", newValue);
Direct property access without wrapper functions. Cleaner interop code.
How Did QuickGrid Improve?
Conditional row styling:
<QuickGrid Items="@customers" RowClass="GetRowClass">
<PropertyColumn Property="@(c => c.Name)" />
<PropertyColumn Property="@(c => c.Balance)" />
</QuickGrid>
@code {
private string GetRowClass(Customer customer)
{
return customer.Balance < 0 ? "negative-balance" : "";
}
}
Programmatically close column options:
await quickGrid.HideColumnOptionsAsync();
What Changed with NavLink?
NavLink with NavLinkMatch.All now correctly handles query strings and fragments:
@* Both now correctly show as active *@
<NavLink href="/products" Match="NavLinkMatch.All">Products</NavLink>
@* Works with: *@
@* /products *@
@* /products?category=electronics *@
@* /products#featured *@
Previous versions had inconsistent matching. Now it works as expected.
How Does Reconnection UI Work?
New templates include a customizable ReconnectModal component:
@* ReconnectModal.razor — included in templates *@
<div class="reconnect-modal" @attributes="AdditionalAttributes">
<div class="reconnect-content">
<p>Connection lost. Attempting to reconnect...</p>
<button @onclick="Retry">Retry Now</button>
</div>
</div>
Key improvements:
- No programmatic style insertion (CSP compliant)
- Collocated stylesheet and JavaScript
- Full customization control
What About Preloading?
Blazor WebAssembly preloading is enhanced:
<!-- In index.html -->
<link rel="preload" href="_framework/blazor.webassembly.js" as="script" />
Combined with the smaller bundle size, initial load times improve significantly.
What's New for Blazor Server Specifically?
Circuit state persistence — The headline feature. Users survive disconnections.
Memory pool eviction — Automatic eviction of unused memory pools. Better resource management for long-running apps.
Improved diagnostics — Better error messages and debugging information.
What's New for Blazor WebAssembly?
Smaller runtime — Continued trimming improvements.
Faster startup — Preloading and compression improvements.
Better debugging — Enhanced source maps and debugging experience.
How Do I Upgrade to .NET 10?
<!-- Update TargetFramework in .csproj -->
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
# Install .NET 10 SDK
dotnet --version # Should show 10.0.x
# Update packages
dotnet restore
dotnet build
Most Blazor apps upgrade without code changes. Test thoroughly — some edge cases may need adjustment.
Should I Use Blazor Server or WebAssembly?
.NET 10 improvements favor both:
| Scenario | Recommendation |
|---|---|
| Internal apps | Server (simpler deployment) |
| Public-facing | WebAssembly (better scalability) |
| Offline support | WebAssembly (required) |
| Fast initial load | Server (no WASM download) |
| Lower server costs | WebAssembly (client-side compute) |
Blazor United (hybrid rendering) works well for most scenarios.
Quick Reference: .NET 10 Blazor Features
| Feature | Description |
|---|---|
[PersistentState] |
Persist component state across reconnections |
| Smaller bundle | 76% reduction in blazor.web.js |
| Passkey auth | WebAuthn/FIDO2 password-less login |
| Nested validation |
[ValidatableType] for complex forms |
NotFound() |
Automatic 404 handling |
| JS property access | Direct read/write without wrappers |
RowClass |
QuickGrid conditional styling |
| NavLink fixes | Correct query/fragment matching |
| ReconnectModal | CSP-compliant reconnection UI |
.NET 10 is an LTS release — three years of support through November 2028. Upgrade when ready.
Written by Jacob Mellor, CTO at Iron Software. Jacob created IronPDF and leads a team of 50+ engineers building .NET document processing libraries.
Top comments (0)