What’s New in .NET 10: The Coolest Features Across Runtime, C# 14, AI, Blazor, and MAUI

.NET 10 marks a major leap forward for developers, combining unprecedented performance with modern language features and next-generation tooling.

With C# 14, developers can enjoy field-backed properties, extension properties, and ref‑enabled lambdas, making code more expressive, concise, and memory-efficient.

Under the hood, .NET 10 delivers smarter JIT optimizations, faster loops, improved memory management, and Native AOT compilation, enabling lightning-fast web, desktop, and microservice applications.

Blazor gains persistent state, enhanced routing, and QuickGrid improvements for richer web UIs, while .NET MAUI offers faster startup, new cross-platform controls, and hot reload for mobile and desktop development.

On top of that, built-in AI integration and multi-agent frameworks bring first-class support for intelligent, cloud-native applications. Together, these innovations make .NET 10 not just an upgrade, but a platform for building the fastest, most modern, and most capable applications yet.

Here’s a guided tour of the coolest features, complete with code examples.

Runtime & Performance Upgrades

.NET 10 introduces massive improvements under the hood:

  • Faster loops and better JIT optimizations — the runtime now aggressively in lines and rearranges hot paths for optimal execution.
  • Smarter memory management and GC — smaller allocations, lower latency, and better pause times.
  • Native AOT compilation — apps start faster and have smaller footprints, perfect for microservices, edge apps, and containers.

Example: Optimized Loop

Even simple loops like this run noticeably faster thanks to advanced JIT heuristics and memory optimizations.

int Sum(int[] numbers)
{
int total = 0;
foreach (var n in numbers)
total += n;
return total;
}

// JIT in .NET 10 optimizes this loop for maximum speed

C# 14 Language Features

.NET 10 comes paired with C# 14, which makes your code cleaner, safer, and more expressive.

Field-Backed Properties

field-backed property in C# is a property that stores its value in a private field rather than being fully auto-implemented or computed. This gives you control over how values are stored or validated while keeping the convenient property syntax. Essentially, the property “backs” its value with an actual field in memory.

Example: Field-Backed Properties

This example defines a Product class with a price property and a field-backed Discount property that automatically clamps its value between 0 and 1 when set.

public class Product
{
public decimal Price { get; private set; } = 0m;
public decimal Discount
{
get => field;
set => field = Math.Clamp(value, 0m, 1m);
}
}

Extension Properties

You’re probably familiar with extension methods, which allow you to “add” new methods to existing types without modifying their original class. Extension properties do the same thing, but for properties.

In other words, you can add a property to a type you don’t own (like stringDateTime, or a third-party class) and access it as if it were part of the original type.

Example: Extension Properties

This example defines a static UserExtensions class with a static property FullName, allowing a single shared value across the application.

public static class UserExtensions
{
public static string FullName { get; set; }
}

Ref / In Lambdas

In earlier C# versions, lambda parameters were always passed by value by default. That means for large structs or performance-critical scenarios, passing them could create unnecessary copies, which can slow down code or increase memory usage.

With ref and in lambdas, you can now pass parameters by reference while still using lambda expressions. This gives you both safety and efficiency.

ref Lambdas

Pass the parameter by reference, allowing you to read and modify the original value.

Example: ref Lambdas

This example demonstrates a ref lambda, where the lambda parameter is passed by reference, allowing the lambda to modify the original variable directly, doubling number from 10 to 20.

int number = 10;
Action<ref int> multiply = (ref int x) => x *= 2;

multiply(ref number);
Console.WriteLine(number); // Output: 20These features reduce boilerplate and give you more control over performance-critical scenarios.
  • Useful when you want mutations to affect the original variable inside a lambda.
  • Works great in performance-critical loops, arrays, or struct-heavy code.

in Lambdas

Pass the parameter by reference, but read-only. This prevents copying large structs while keeping them immutable inside the lambda.

Example: in Lambda

This example defines a LargeStruct and uses an in lambda to pass it by read-only reference, efficiently computing and printing the sum of its fields without copying the struct.

struct LargeStruct
{
public int A, B, C, D;
}

LargeStruct data = new LargeStruct { A = 1, B = 2, C = 3, D = 4 };

Action<in LargeStruct> printSum = (in LargeStruct x) =>
{
Console.WriteLine(x.A + x.B + x.C + x.D);
};

printSum(data); // Output: 10
  • Efficient for struct-heavy operations (like numeric calculations, vectors, or graphics data).
  • Guarantees safety, since the lambda cannot modify the original object.

Partial Constructors

You can now split constructors across partial classes, just like methods or properties. This is useful for code generation scenarios or large classes where constructors can be split across files.

Example: Partial Constructor

This example demonstrates partial constructors, splitting a Product class across two files with separate constructors for Name and Price, allowing modular and maintainable initialization logic.

// Part1.cs
public partial class Product
{
public string Name { get; }
public Product(string name) => Name = name;
}

// Part2.cs
public partial class Product
{
public decimal Price { get; set; }
public Product(decimal price) => Price = price;
}

This allows more modular and maintainable construction logic in large codebases.

Partial Events

You can now define events in partial classes and implement them across multiple files. This helps with separating concerns in large classes that manage multiple event sources.

Example: Partial Event

This example demonstrates partial events, defining an event in one part of a Notifier class and implementing its trigger logic in another part, allowing event handling to be split across multiple files.

// Part1.cs
public partial class Notifier
{
public partial event EventHandler? OnNotify;
}

// Part2.cs
public partial class Notifier
{
public void Trigger() => OnNotify?.Invoke(this, EventArgs.Empty);
}

Ref-Struct Interface Support

C# 14 allows ref structs (like Span<T>) to implement interfaces. Previously, ref structs were limited because of stack-only constraints. Now you can abstract and reuse stack-only types safely.

Example: ref-Struct Interface Support

This example defines a ref struct SpanProcessor that implements an interface, allowing stack-only data (Span<int>) to be processed while enforcing high-performance, safe memory usage.

public interface IProcessor
{
void Process();
}

public ref struct SpanProcessor : IProcessor
{
private Span<int> _data;
public SpanProcessor(Span<int> data) => _data = data;
public void Process() => _data[0] = 42;
}

This makes working with high-performance, stack-allocated data more flexible.

Improved Overload Resolution

The compiler is smarter about choosing the best overload, especially with generics, lambdas, and extension methods. This results in less ambiguity, fewer explicit casts, smoother development experience.

Example: Improved Overload Resolution

This example demonstrates improved overload resolution, where the compiler automatically selects the correct Print method based on the argument type, distinguishing between int and double.

void Print(int x) => Console.WriteLine("Int");
void Print(double x) => Console.WriteLine("Double");

Print(3); // Automatically resolves to int
Print(3.0); // Automatically resolves to double

It may seem minor, but it reduces subtle bugs and verbosity in large APIs.

Refined Tuples & Patterns

Tuples and pattern matching get small but handy enhancements:

  • Deconstruction in more places
  • Better type inference with nested tuples
  • Pattern matching on in parameters

Example: Nested Tuples with Pattern Matching

This example uses nested tuple pattern matching to extract values from a tuple and check that the Math score is at least 80, then prints the student’s name and English score.

var data = (Name: "Alice", Scores: (Math: 90, English: 85));

if (data is (var name, (>= 80, var englishScore)))
{
Console.WriteLine($"{name} has strong scores, English: {englishScore}");
}

Example: Discarding Values

This example uses a tuple pattern with a discard _ to check that Y is positive while ignoring the value of X.

var point = (X: 5, Y: 10);

if (point is (_, > 0))
{
Console.WriteLine("Y is positive, X value ignored.");
}

Example: Using Tuples in Switch Statements

This example uses a tuple in a switch expression to determine movement direction based on X and Y values, producing "Moving Up" for the given coordinates.

var direction = (X: 0, Y: 1);

string result = direction switch
{
(0, > 0) => "Moving Up",
(0, < 0) => "Moving Down",
(> 0, 0) => "Moving Right",
(< 0, 0) => "Moving Left",
_ => "Stationary"
};

Console.WriteLine(result); // Output: Moving Up

Example: Pattern Matching with Deconstruction

This example defines a Rectangle record and uses pattern matching to check if its width is greater than 5 and height greater than 15, printing a message if both conditions are met.

record Rectangle(int Width, int Height);

var rect = new Rectangle(10, 20);

if (rect is { Width: > 5, Height: > 15 })
{
Console.WriteLine("Large rectangle detected!");
}

Example: Combining Tuples and Type Patterns

This example uses tuple pattern matching with types to check that obj is a tuple containing an int and a string, then extracts and prints their values.

object obj = (42, "Answer");

if (obj is (int number, string text))
{
Console.WriteLine($"Number: {number}, Text: {text}");
}

Benefits:

  • Reduces boilerplate and improves readability for multi-value conditions.
  • Works with tuples, records, and deconstructable objects.
  • Makes complex decision logic more concise and expressive.

ASP.NET Core & Minimal APIs Improvements

.NET 10 enhances web development with minimal APIs, real-time updates, and stronger security.

Server-Sent Events (SSE)

Server-Sent Events are a lightweight, uni-directional mechanism for sending real-time updates from a server to the browser over HTTP. Unlike WebSockets:

  • SSE is one-way: server → client
  • It uses standard HTTP (no special protocol)
  • Supports automatic reconnection and event IDs
  • Great for live dashboards, notifications, stock tickers, or real-time logs

Example: Server Side Event (SSE)

This example sets up a Server-Sent Events (SSE) endpoint that continuously streams the current UTC time to connected clients in real time.

app.MapGet("/events", () =>
TypedResults.ServerSentEvents(async (stream, ct) =>
{
await stream.SendEventAsync("tick", DateTime.UtcNow.ToString(), ct);
}));

Benefits:

  • Simple to implement
  • Works with existing HTTP infrastructure (proxies, load balancers)
  • Lightweight for low-latency updates
  • Perfect for live feeds that don’t require full duplex communication

Passkey / WebAuthn Support

.NET 10 introduces first-class support for WebAuthn / Passkeys, making it easier than ever to implement passwordless login in ASP.NET Core applications.

  • WebAuthn is a web standard for passwordless authentication.
  • Passkeys are the user-friendly implementation of WebAuthn — stored securely on devices (phones, laptops, security keys).
  • Users can log in with biometrics (fingerprint/face) or a hardware token, rather than a password.
  • Security is stronger, phishing-resistant, and modern.

Register a Passkey Example

This example defines a WebAuthn/Passkey registration endpoint that generates and returns credential creation options for the currently logged-in user, enabling passwordless authentication.

// Endpoint to begin registration
app.MapPost("/webauthn/register", async (UserManager<IdentityUser> userManager, HttpContext http) =>
{
var user = await userManager.GetUserAsync(http.User);
var options = new CredentialCreateOptions
{
User = new PublicKeyCredentialUserEntity
{
Id = user.Id,
Name = user.UserName
},
AuthenticatorSelection = new AuthenticatorSelection
{
RequireResidentKey = true,
UserVerification = UserVerificationRequirement.Required
}
};

return Results.Json(options);
});

Blazor Highlights

Blazor continues to shine in .NET 10, making interactive web apps faster, more resilient, and easier to develop.

Persistent State

In Blazor (both Server-Side / SSR and WebAssembly), components have state — e.g., the value of a counter, a selected tab, or user input. Normally, if the page refreshes or the user navigates away, that state is lost.

Persistent State allows you to automatically persist a component’s state across:

  • Page reloads
  • Navigation between pages
  • Browser refreshes

This makes Blazor apps feel smoother, more robust, and “stateful” without extra manual coding.

  • You decorate a property in your component with [PersistentState].
  • Blazor automatically saves and restores the value.
  • The framework handles serialization and storage behind the scenes (local storage for WASM, in-memory or session for SSR).

Example: Blazor PersistentComponentState

This Blazor component demonstrates persistent state, where the Counter property retains its value across page reloads or navigation using the [PersistentState] attribute.

@page "/counter"
@inject PersistentComponentState AppState

<h3>Counter</h3>

<p>Current count: @Counter</p>
<button @onclick="IncrementCount">Increment</button>

@code {
[PersistentState]
private int Counter { get; set; } = 0;

private void IncrementCount()
{
Counter++;
}
}

Benefits:

  • Less boilerplate: No need to manually serialize state to localStorage or a database.
  • Seamless UX: Users can navigate or refresh without losing data.
  • Flexible storage: Works with both Blazor Server and WebAssembly.
  • Combine with other Blazor features: Routing, forms, and multi-step flows are easier to implement.

You can also inject PersistentComponentState to manually persist or restore state for non-attribute scenarios:

Example: Manual Persistent State with JSON

This code manually persists and restores a Blazor component’s state as JSON, allowing myObject to be saved with a key and later retrieved safely.

AppState.PersistAsJson("CustomKey", myObject);
var restored = AppState.TryRestoreJson<MyType>("CustomKey");

This is handy for complex objects or shared state between components.

Enhanced Routing & Fallback Pages

Routing in Blazor controls how the app maps URLs to components. In .NET 10, routing gets some useful improvements:

  • Fallback pages: Easily handle URLs that don’t match any route.
  • Improved route resolution: Faster and more predictable mapping of routes, especially for complex apps.
  • Flexible NotFound handling: Let users see a “friendly” 404 page instead of a blank screen.

Example: Blazor Fallback Page for Unmatched Routes

This example demonstrates how to redirect any unmatched URL to a custom page, such as a 404 or NotFound page.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapFallbackToPage("/NotFound");

app.Run();
  • Any unmatched route will render the /NotFound page.
  • Avoids users getting stuck on blank pages or errors.

These improvements make large SPAs or hybrid apps feel more polished and professional.

Blazor Component Example

You can also define a NotFound component directly in Blazor.

Example: Blazor Router with Custom NotFound Component

This example demonstrates how to handle unmatched routes gracefully by displaying a user-friendly 404 page within a Blazor app.

<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<h3>Oops! Page not found.</h3>
<p>The page you’re looking for does not exist.</p>
</LayoutView>
</NotFound>
</Router>
  • <NotFound> renders automatically when no routes match.
  • You can customize layout, styling, and messaging for a better user experience.

Enhanced Route Matching Features

  • Route parameters are more predictable: Handles optional parameters, constraints, and complex patterns better.
  • Catch-all routes: Use {*path} to capture all remaining segments.
  • Hybrid fallback: Works with Blazor Server, WASM, and SSR seamlessly.

Example: Blazor Catch All Route

This example demonstrates how to capture any remaining URL segments into a parameter for dynamic routing, such as blog posts or CMS pages.

  • Any URL starting with /blog/ maps to this component.
  • Helps with dynamic pages or CMS-style routing.
@page "/blog/{*slug}"
<h3>Blog Page: @slug</h3>

@code {
[Parameter] public string slug { get; set; }
}

Benefits:

  • Better user experience: Users see a clear 404 page instead of blank content.
  • Simplifies complex routing: Makes dynamic URLs and multi-level paths easy to handle.
  • Consistent across hosting models: Works with SSR, WASM, or hybrid Blazor apps.
  • Easier to maintain: Centralized fallback reduces boilerplate checks in components.

QuickGrid Improvements

QuickGrid is a Blazor component for displaying tabular data quickly and efficiently.

It’s designed to be lightweight, high-performance, and flexible, with features like:

  • Sorting
  • Paging
  • Custom columns
  • Minimal setup

.NET 10 brings enhancements that make QuickGrid more powerful and easier to use, especially for large datasets.

Example: QuickGrid

This example demonstrates how to display a simple list of products in a table using the QuickGrid component with automatic column mapping.

<QuickGrid Items="products">
<PropertyColumn Property="p => p.Name" Title="Product Name" />
<PropertyColumn Property="p => p.Price" Title="Price" />
</QuickGrid>

@code {
private List<Product> products = new()
{
new Product { Name = "Laptop", Price = 1200 },
new Product { Name = "Mouse", Price = 25 },
new Product { Name = "Keyboard", Price = 75 }
};
}
  • Minimal markup, automatic rendering of rows.
  • PropertyColumn maps properties to columns easily.

Sorting

Columns can now sort automatically with very little setup.\

Example: Sortable Column in Blazor QuickGrid

This example demonstrates how to enable sorting on a specific column within a QuickGrid table.

<PropertyColumn Property="p => p.Price" Title="Price" Sortable="true" />

Paging / Virtualization

  • QuickGrid supports large datasets with paging or virtualized rendering.
  • Improves performance for grids with thousands of rows.

Example: QuickGrid with Paging and Virtualization

  • QuickGrid is bound to a large list of products.
  • ItemsProvider handles virtualization by loading only the visible items for each page.
  • PageSize="20" ensures only 20 rows are rendered at a time for performance.
  • As the user scrolls or changes pages, LoadProducts fetches the appropriate slice of data.
@page "/products-grid"
@using System.Linq

<h3>Products Grid with Virtualization</h3>

<QuickGrid Items="@PagedProducts" ItemsProvider="LoadProducts" TItem="Product" PageSize="20">
<PropertyColumn Property="p => p.Name" Title="Product Name" />
<PropertyColumn Property="p => p.Price" Title="Price" />
</QuickGrid>

@code {
private List<Product> allProducts = new();
private int totalItems;

protected override void OnInitialized()
{
// Simulate a large dataset
for (int i = 1; i <= 1000; i++)
{
allProducts.Add(new Product { Name = $"Product {i}", Price = i * 1.5m });
}
totalItems = allProducts.Count;
}

// Virtualized paging function
private async ValueTask<ItemsProviderResult<Product>> LoadProducts(ItemsProviderRequest request)
{
// Simulate async loading
await Task.Delay(50);

var data = allProducts
.Skip(request.StartIndex)
.Take(request.Count)
.ToList();

return new ItemsProviderResult<Product>(data, totalItems);
}

private IEnumerable<Product> PagedProducts => allProducts.Take(20); // default first page
}

public class Product
{
public string Name { get; set; } = "";
public decimal Price { get; set; }
}

Benefits:

  • Efficiently handles thousands of rows without slowing down the UI.
  • Paging and virtualization reduce memory and rendering overhead.
  • Works seamlessly with Blazor Server, WASM, and SSR apps.

Custom Column Templates

You can define custom content for each column using a template.

Example: Custom Column Template in Blazor QuickGrid

This example demonstrates how to define a column with a custom template to display computed values, such as a 10% discount of the product price.

<PropertyColumn Title="Discount">
<Template Context="p">
@(p.Price * 0.1):C
</Template>
</PropertyColumn>

Shows a computed value (10% discount) without changing the underlying data model.

Better Styling and Responsiveness

  • Columns and headers can now be styled more easily.
  • Integrates well with CSS frameworks like Tailwind, Bootstrap, or custom Blazor styles.

Benefits:

  • Blazing fast: Handles large datasets efficiently.
  • Minimal boilerplate: Most common tasks (sorting, paging, column display) are automatic.
  • Customizable: Templates and styles allow you to tailor the UI exactly as needed.
  • Perfect for dashboards: Works well with Blazor SSR, WASM, and hybrid apps.

.NET MAUI Improvements

MAUI continues to evolve as the go-to framework for building truly cross-platform apps — mobile, desktop, and beyond. .NET 10 introduces performance enhancements, richer controls, and smoother developer experiences.

Key Highlights

Faster Startup & Layout Passes

  • Apps launch faster across Windows, Mac, iOS, and Android.
  • Layout calculations are more efficient, reducing render delays for complex UIs.

New Controls & Handlers

  • MenuFlyout, Maps, WebView improvements, CarouselView, and advanced Button styles.
  • Desktop-specific improvements for Windows and Mac, including better native integration.

Hot Reload & Improved Tooling

  • XAML and C# Hot Reload lets you instantly see UI changes without rebuilding.
  • Enhanced debugging, designer, and IntelliSense support for cross-platform projects.

Graphics & Animations

  • Improved graphics rendering with GraphicsView.
  • Smooth, high-performance animations and transitions for interactive apps.

Cross-Platform Device Features

  • Simplified access to sensors, cameras, GPS, and file system APIs.
  • Unified approach across platforms reduces boilerplate and platform-specific code.

MVU (Model-View-Update) Support (optional but cool)

  • Experimental support for declarative UI patterns for even faster prototyping.

Example: Simple MAUI Button

This example demonstrates creating a cross-platform button with styling and a click event that displays an alert.

var button = new Button
{
Text = "Say Hello",
BackgroundColor = Colors.LightBlue,
CornerRadius = 8
};

button.Clicked += (s, e) =>
{
Shell.Current.DisplayAlert("Hello", "Welcome to .NET 10!", "OK");
};

Navigation Example

This example demonstrates how to navigate to another page in a MAUI app using the Shell routing system when a button is clicked.

var nextButton = new Button { Text = "Go to Details" };
nextButton.Clicked += async (s, e) =>
{
await Shell.Current.GoToAsync("detailspage");
};
  • MAUI’s Shell navigation is now faster and more consistent across devices.
  • Supports parameter passing, query strings, and deep linking.

Benefits:

  • Build cross-platform apps once with native performance.
  • Hot Reload + faster startup = instant feedback for developers.
  • More controls and graphics support = rich, interactive UIs.
  • Unified device APIs = less platform-specific boilerplate.
  • MAUI in .NET 10 is ready for production-grade mobile, desktop, and hybrid apps.

AI Integration & Agent Framework

.NET 10 introduces first-class AI support, making it easier than ever to embed intelligent agents, conversational AI, and automated workflows directly into your applications. With multi-agent frameworks and unified AI APIs, developers can orchestrate AI tasks seamlessly across platforms, from cloud services to local applications.

Key Highlights

Multi-Agent Frameworks

  • Coordinate multiple AI agents to perform complex tasks.
  • Agents can handle specific roles, like summarizing text, fetching data, or making decisions.
  • Enables composable AI workflows for enterprise apps, chatbots, and digital assistants.

Unified AI Client

  • Access models from Azure OpenAI, OpenAI, or other providers through a single API.
  • Simplifies authentication, request handling, and result processing.
  • Allows easy swapping or upgrading models without changing your app logic.

Conversational AI & Intelligent Agents

  • Create chatbots, virtual assistants, or workflow agents with minimal code.
  • Support for long-running context, memory, and multi-step reasoning.
  • Integrates with Blazor, MAUI, and server apps, so AI works wherever your app runs.

Productivity & Automation

  • Automatically generate summaries, analyze data, or create content.
  • Orchestrate AI-driven tasks alongside traditional business logic.
  • Reduce boilerplate for AI integration, letting developers focus on application features.

Minimal AI Example

This makes it easier than ever to add intelligent agents or conversational AI to your applications.

  • The unified API allows you to manage agents and models consistently.
  • Works with Blazor, MAUI, ASP.NET Core, and background services.
  • Makes building intelligent, interactive, or automated workflows straightforward.

Example: AI Integration with Multi-Agent Framework in .NET 10

This example demonstrates using a unified AI client to send prompts and orchestrate multiple intelligent agents for tasks like summarization and research.

var client = builder.Services.AddOpenAIChatClient("gpt-4.2");

// Simple prompt
var answer = await client.CompleteAsync("Explain Blazor in one sentence.");
Console.WriteLine(answer);

// Example with multi-agent orchestration
var summaryAgent = client.CreateAgent("Summarizer");
var researchAgent = client.CreateAgent("Researcher");

await summaryAgent.SendAsync("Summarize this article...");
await researchAgent.SendAsync("Find additional references...");

Benefits:

  • Integrated AI: No need to manually handle multiple APIs or SDKs.
  • Multi-agent orchestration: Coordinate complex tasks intelligently.
  • Cross-platform ready: Works in web apps, desktop apps, and mobile apps.
  • Rapid development: Build intelligent features with minimal code.
  • Future-ready: Supports emerging AI models and workflows in a consistent way.

.NET 10 isn’t just an incremental update — it’s a platform-wide leap forward. Developers gain blazing runtime performance with smarter JIT optimizations, faster loops, improved memory management, and Native AOT compilation. At the same time, C# 14 introduces cleaner, more expressive code features, including field-backed properties, extension properties, and ref‑enabled lambdas, making code both safer and more maintainable.

Blazor now makes building interactive web applications smoother and more robust with features like persistent state, enhanced routing, and QuickGrid improvements. Meanwhile, .NET MAUI streamlines cross-platform mobile and desktop development, offering faster startup, new desktop and mobile controls, and Hot Reload for rapid iteration.

On top of that, built-in AI integration and multi-agent frameworks empower developers to build intelligent, cloud-native applications with minimal friction. Together, these innovations position .NET 10 as the fastest, most versatile, and future-ready platform yet, giving developers everything they need to create modern applications that are powerful, efficient, and ready for the AI-driven world.