Telerik blogs

.NET 9 is shaping up ahead of its November 2024 release. Here are the highlights for Blazor so far.

.NET 8 was a big release for Blazor, introducing new interactive render modes and an option to build entire web apps using static server-side rendering. It was also an LTS (Long Term Support) release, meaning it’s fully supported by Microsoft until November 2026.

Now Microsoft is focused on delivering a smaller set of changes, tweaks and improvements for Blazor in the upcoming .NET 9 release, slated for November 2024. This will be a STS (Standard Term Support) release with support for 18 months post-launch.

Here are some of the key Blazor changes released so far (as of Preview 4).

Add Static SSR Pages to Interactive Blazor Web Apps

One of the biggest changes in .NET 8 is the ability to run your app using different render modes.

You can opt to run your pages using static server-side rendering, where the component is rendered on the server and plain HTML returned to the browser.

Or you can opt for one of the interactive render modes (Server, WASM or Auto).

In .NET 8, it’s possible to configure your entire app to run in one mode, globally. For example, if you want your app to run using Interactive Server mode. But then you’re stuck with that interactive render mode for all pages/components in your app.

In .NET 9, it will be possible to configure a page component to run using static SSR, even within an app which is otherwise configured to run interactively.

It’s worth saying this is something you would want to avoid in most cases. It will force a full page reload, which is less efficient and responsive than the same component running interactively (where Blazor can be efficient about fetching the UI and patching it into the browser’s DOM).

But this will be useful when you absolutely need a specific page to run this way, for example if you need it to read/write HTTP cookies.

How Routing Typically Works for a Blazor App

To understand how this works, consider this typical App.razor component:

<!DOCTYPE html>
<html>
<head>
    ...
    <HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
    <Routes @rendermode="InteractiveServer" />
    ...
</body>
</html>

When a user attempts to navigate to a specific page (e.g., “/counter”), this top-level component will be rendered. In turn it will render the Routes component using the specified @rendermode.

In this case that means all routing will be handled by our Routes component which is running interactively using Blazor server.

Switching to Static SSR for Individual Pages

To make this page in our app run using static server-side rendering, we need a way to force this one request (for our non-interactive page) to be handled by a router that is running “non-interactively” (using static SSR).

We can do that in .NET 9 via two steps:

  • Indicate that a component should be excluded from interactive routing.
  • Modify App.razor to use static SSR when routing to that component.

The first step is to exclude the component from using Blazor’s interactive router.

PageToBeRenderedUsingSSR.razor

@attribute [ExcludeFromInteractiveRouting]

By decorating a page with the ExcludeFromInteractiveRouting attribute, it will be excluded from interactive routing. However, this attribute by itself won’t do much.

Next we need to modify App.razor so the Routes component will use static SSR as its render mode for this component.

Here’s an example, taken from the official docs:

<!DOCTYPE html>
<html>
<head>
    ...
    <HeadOutlet @rendermode="@PageRenderMode" />
</head>
<body>
    <Routes @rendermode="@PageRenderMode" />
    ...
</body>
</html>

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? PageRenderMode
        => HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}

This brings in a parameter for HttpContext.

We’re then able to call the new HttpContext.AcceptsInteractiveRouting method to check if the current request is for a page that accepts interactive routing (based on the ExcludeFromInteractiveRouting attribute).

If so, it will return InteractiveServer as the render mode; otherwise it returns null. null indicates that the component should be rendered using static SSR. Our Routes component will then be rendered using the relevant render mode.

More Efficient WebSocket Connections for Blazor Server

In .NET 9, your Interactive Server components will now enable compression for the underlying WebSocket connections by default.

This will result in reduced bandwidth usage (less data being transmitted to and received back from the server). As a result, you should see faster data transfer and lower latency when interacting with these components.

However, using compression with WebSocket connections increases the vulnerability of an app to side-channel attacks (where an attacker can attack the TLS encryption of the connection). If you want to dig into why that’s the case, check out the official docs here for interacting with these components).

To counter this increased security risk, these components will also specify a frame-ancestors Content Security Policy (CSP) of self.

CSPs are an an important security feature that specify which sources of content are allowed to be loaded and executed by a webpage. Here the frame-ancestors CSP means the component can only be embedded in an iframe with the same origin as the app serving the component with compression enabled.

This restriction prevents someone from embedding a page from your Blazor app in an iframe on a different site, thereby reducing the chances of a successful side-channel attack.

Of course, if you want to disable this compression, you can:

.AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null)

You can also tighten up security further by blocking all embedding in an iframe.

.AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy = "'none'")

Constructor Injection

There are a few places where you can put the code for your C# Razor components.

You can use a @code {} block in the .razor file itself.

Greeting.razor

<h2>Hello @name</h2>
    
@code {    
    private string name = "Alice";    
}

Alternatively you might opt to declare a separate class (in this case by using the partial keyword).

Greeting.razor.cs

public partial class Greeting(){
    
	private string name = "Alice";
    
}

With the latter approach, you’ve always been able to inject dependencies using property injection.

Say, for example, we want to inject and use NavigationManager (to programmatically navigate to a different page).

Greeting.razor.cs

using Microsoft.AspNetCore.Components;

namespace BlazorExperiments.Components.Pages;

public partial class Greeting
{
    private string name = "Alice";

    [Inject] protected NavigationManager NavMan { get; set; }

    private void NavigateHome()
    {
        NavMan.NavigateTo("/");
    }
}

This injects an instance of NavigationManager via the NavMan property (then invokes it from the NavigateHome method).

In the UI, we can call NavigateHome to trigger the navigation.

Greeting.razor

@page "/greeting"
@rendermode InteractiveServer

<h2>Hello @name</h2>

<button @onclick="NavigateHome">Go Home</button>

The above example will work in .NET 8 (and earlier versions of .NET).

Now in .NET 9 there’s a new option to inject dependencies via constructor injection:

public partial class Greeting(NavigationManager navMan)
{
    private string name = "Alice";

    private void NavigateHome()
    {
        navMan.NavigateTo("/");
    }
}

This uses C# 12’s primary constructors, but you can of course use a regular constructor here too.

Support for Composition Events

You could be forgiven for not knowing about composition events (I didn’t!), but it turns out they’re important when handling international character input methods. Languages like Chinese, Japanese and Korean have massive sets of characters (thousands) that aren’t easily mapped to a standard keyboard layout.

For those languages, you can use an Input Method Editor (IME). An IME enables users to type sequences of keys that are then converted into the desired characters. This is a multi-step composition process, where the user starts the composition, selects various characters, then finalizes the input.

Composition events are fired when the IME starts a new composition session, updates the session with a new input and eventually finalizes the composition session.

To properly handle user input in these scenarios, you need to be able to distinguish between regular key events and those that are part of a composition session.

.NET 9 will introduce a KeyboardEventArgs.IsComposing property to indicate if a keyboard event is part of a composition session.

It will then be possible to use that to trigger the appropriate logic for your specific components.

OverscanCount for QuickGrids

Finally, we have a new OverscanCount parameter for the QuickGrid component.

QuickGrid is a handy way to quickly show data in your Blazor components.

It also supports virtualization, where you can load an initial amount of data and then, as the user scrolls down through the data, fetch more and load that into the existing HTML.

This results in a seemingly infinite scroll for the user, without blowing up your browser by attempting to load potentially thousands, or even millions, of rows of data all at once.

To reduce the number of times it has to render, QuickGrid will typically render a number of additional items before and after the visible region.

Say the user is viewing rows 20 to 30. QuickGrid will also render a number of rows before row 10, and after row 30.

The number of rows is determined by the OverscanCount. If the OverscanCount is set to 5, in our example QuickGrid would render rows 15 to 35. It doesn’t then need to render again until the user scrolls up to (or above) row 14 or down to (or past) row 36.

The downside of a large overscan count is that it can result in an increase in initial load times so the trick is to find a balance based on your data and optimal experience for the user.

Small Quality of Life Improvements

So there we have it, a handful of small but useful and important enhancements for Blazor in .NET 9.

After the big changes in .NET 8, it’s perhaps no surprise that we’re seeing smaller, incremental improvements in .NET 9.

That said, there are some significant items still to come according to the .NET 9 roadmap, including:

  • Improvements to persistent state when using enhanced navigation
  • Simplified (declarative) method to persist state between prerendering and interactive rendering
  • Inclusion of Microsoft Identity Platform auth as an option in the Blazor web app template
  • Improved reconnection logic for Blazor Server

For now, .NET 9 Preview 4 smooths out some of Blazor’s rougher edges.

From here, we can expect a few more preview releases, a couple of release candidates, and then .NET 9 will ship in November 2024.


Jon Hilton
About the Author

Jon Hilton

Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them. Follow him on Twitter here.

Related Posts

Comments

Comments are disabled in preview mode.