Server Modes in Blazor 8

12/19/2023

Server Modes in Blazor 8

Using Server Interactive and SSR together in a Blazor 8 web app

Blazor 8 just came out and if you're like me, you found yourself having to upgrade your project(s) in short order for various reasons. The timing couldn't be better, as our Blazor projects require the need to have both Blazor Interactive Server as well as Server Side Rendering (SSR) for SEO purposes.

Now, when you start up a new project, you'll find that you can have both of these modes working seamlessly together, simply by declaring @rendermode InteractiveServer in the page where you need it.

Perfect. Beautiful. Naturally, nothing worked at first blush when attempting to upgrade from 6 to 8.

The app we're putting together is a fairly complicated one where a good 80% of it is back end work - settings pages and the like that have been painstakingly put together to support some rather complex setup logic in order to display a couple of SEO-optimized profile pages. There's complex components with layers for authentication, logging and other bits that go into the back end. We're also using .cs files with our pages to keep the code separated from the display logic.

No matter where we put our @rendermode declarations, we were not kicking on the WebSockets, and that was a problem. The most common problem was this message in the console:

Failed to load resource: net::ERR_HTTP2_PROTOCOL_ERROR

followed by lots of this:

 Connection id "0HN07NB86IBVG", Request id "0HN07NB86IBVG:0000000F": An unhandled exception was thrown by the application.
System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (65536 of 186283)

The Fix

** UPDATE ** If you read the original version of this, we got this wrong. Very wrong.

To correct our previous blunder, there is no blazor.server.js to declare, anywhere. You simply use blazor.web.js. Not sure how or what we got working in hindsight, but it was enough to inspire writing about it in the first place.

The fix for this, in the end, was to ~~migrate the application to a new Blazor 8 project~~ use the process of elimination to make sure you don't have anything creating WebSocketExceptions. In our case, it turned out a seemingly harmless IMiddleware implementation adding context to our Serilog logging was the culprit, specifically our call to app.UseMiddleware<> which simply failed no matter what sequence we put it in - the Microsoft documentation on Middleware sequencing was of no use to us here; in fact, it seems to eschew app.UseMiddleware<>() for setting up middleware in the Program.cs file itself. Maybe there's something to that...

RenderMode in Context

As it turns out, Blazor 8 supports SSR, InteractiveServer and InteractiveAuto in the same project. It's there by default -- all you have to do is declare @rendermode InteractiveServer in your pages and your page now supports Websockets. Keep in mind you'll need a separate Client project for InteractiveAuto pages, and you'll need to set up the references like such:

There does seem to be a catch, however, and the catch does not appear to be very clear or straightforward. And while there's already plenty written about @rendermode already, let's point out a couple of things that might not be clear if you're upgrading in a hurry.

You've got InteractiveServer, InteractiveAuto, and InteractiveWebAssembly as your options. If you have a particular need, you can set up a custom render option that turns off prerendering in the constructor. So far in our Syncfusion-driven project we haven't needed that.

What is not abundantly clear at first blush is what InteractiveAuto is or how it's supposed to work. For that, you need to initiate a new project template to take a closer look.

First off - it requires a very unique project setup. To really grasp it, you'll need to create a sample project and take a look:

dotnet new blazor -o MyBlazorApp -int Auto

When you do this, you'll see a solution containing two projects - MyBlazorApp.csproj and MyBlazorApp.Client.csproj. The main project doesn't have any pages in there, but in the Program.cs file it has this reference:

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddInteractiveWebAssemblyRenderMode()
    .AddAdditionalAssemblies(typeof(Counter).Assembly);

In the Client project, the standard-issue Counter page is there - it's the only page in the project - and that page has the @rendermode InteractiveAuto declaration in it.

Once you come to realize that detail, the documentation makes a lot more sense.

Speaking of rendering - If you want to turn off prerendering, here's a snippet of code you can use that will help.

public static class CustomRenderModes
{
    public static readonly InteractiveAutoRenderMode InteractiveAutoNoPreRender = new(prerender: false);
    public static readonly InteractiveServerRenderMode InteractiveServerNoPreRender = new(prerender: false);
    public static readonly InteractiveWebAssemblyRenderMode InteractiveWebAssemblyNoPreRender
        = new(prerender: false);
}

Caveat emptor on that one - the recommendation there is to experiment with it once you think you might see a need for it. That goes double for declaring rendermode at the component level. Stick to pages, and then observe on a case-by-case basis. Correction - you can declare render modes at the component level, and it's awesome.

Additional help

Like most Microsoft documentation, there's a lot of "what to do" laid out, but trying to find context behind it is elusive. It's especially frustrating when the only information you get for the upgrade process is "just upgrade the framework and do these bullet points lmao". We ended up taking the project and migrating it into a new project which helped us resolve all of our issues.

If it's not abundantly clear by now, you should definitely take a strong look at the project templates as they're implemented, and experiment there first.

Other places to take a look:

  • John Hinton has a lot of great resources on Blazor 8 and migration. To make sense of some of it, however, we recommend the first step of evaluating new Blazor 8 projects first.
  • Andrew Lock has been writing up some good insights throughout the different versions on how Blazor internals work.
  • Syncfusion Technical FAQ comes up quite a bit when searching for answers to Blazor questions both general and specific to their products. Highly recommend giving them a look.
  • Blazor University is now officially behind the proverbial 8-ball on version 8, but is still a good go-to resource for Blazor fundamentals.

Hope that helps!


Looking for help upgrading your web apps to Blazor? Contact us and let's see what we can do to help!