Prerendering your Blazor WASM application with .NET 5 (part 1)
If, like me, you’re old enough, you probably remember all those elaborate animations that used to blight the web.
Back when Flash ruled the world you would frequently land on a web page only to be shown an interminable, unskippable animation which would actively stop you from proceeding until it was finished.
But wait! Surely those days are behind us? Haven’t we long since moved away from forcing our users to wait their turn?
Loading spinners anyone?
This is very much the case if you use Blazor WASM for your web app.
When users visit your Blazor WASM application for the first time their browser downloads a version of the .NET runtime, plus all your site’s assets (in the form of dlls).
The good news is they only need to do this once because after that the browser has them cached, but this can still leave your users twiddling their thumbs while they wait for the download to complete.
Even then, after they’ve completed the initial download, every time they access your site they’ll encounter a brief “loading” phase while everything spins up.
Prerendering to the rescue
Once possible solution is to prerender your app.
It’s been possible for a while now to prerender your Blazor applications but the overall experience has been updated and improved with .NET 5.
This article has been updated to reflect a couple of of small changes introduced in .NET 5 RC2
The rest of this article builds on the really useful information put out there previously by Chris Sainty and Daniel Roth…
Dan has a very handy looking BlazorNet5Samples repo on Github which is well worth checking out and includes an example of prerendering a Blazor WASM application.
Typically when you interact with a Blazor WASM app the browser first receives an HTML file (
index.html) back from the server, which then takes care of fetching everything else your app needs to run, before spinning up your app via web assembly.
Once the initial request has completed, interacting with your app is generally pretty quick as subsequent requests are all handled right there in the browser (with Blazor figuring out what to render or rerender).
However, during that initial load your users will be left with a loading indicator while they wait for the process to complete.
Prerendering flips this initial request loading around so that it takes place on the server and static HTML is returned to the browser.
This makes for a really snappy first load.
The browser can then download the .NET runtime (if it doesn’t already have it) and your application’s dlls in the background. By the time your users try to interact with the app the chances are it will have finished and everything will have sprung into life!
Prerender your .NET 5 WASM application - step by step
The easiest way to start is by creating a new Blazor WASM project that’s hosted via ASP.NET core.
dotnet new blazorwasm --hosted
When you do this you get three projects:
The Blazor WASM components are all defined in the .Client project.
When you launch the .Server project it serves that client project to the browser.
To prerender the client application you’ll want to start by ditching the
index.html file from .Client/wwwroot.
Instead of that we’re going to use a
.cshtml file in the .Server project to load the app.
Create a file called
_Host.cshtml in the Pages folder in the Server project.
Here’s a minimal example…
<meta charset="utf-8" />
maximum-scale=1.0, user-scalable=no" />
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="ExampleApp.Client.styles.css" rel="stylesheet" />
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
There’s a couple of things to note here.
I’ve added a reference to the main
App component, and set its render-mode to
WebAssemblyPrerendered. This is the new .NET 5 secret sauce that makes sure the component (
App in this case) will be rendered as static HTML and then become ‘dynamic’ once the browser finishes loading Blazor.
Beyond that I’ve also included the
blazor.webassembly.js script. Without this our component(s) would never become interactive.
Finally, I’ve referenced the client app’s stylesheet. This is auto-generated as part of Blazor’s support for CSS Isolation.
You’ll need to make sure you reference your .Client project from the .Server project.
Add a fallback endpoint pointing to
Now we need to make sure all requests to the app are forwarded to
_Host.cshtml if a more specific route isn’t available.
Head over to
Startup.cs and update the line which sets
index.html as the fallback file to point to our Host page instead:
Now when you first attempt to navigate to a page in the application the request will be directed to the new
_Host.cshtml page which will, in turn, prerender the relevant component(s) and return static HTML back to the browser.
Enable Tag Helpers
If you run this in the browser however you might find yourself staring at a blank screen.
If you inspect the source code in the browser you’ll see the raw
This is because that
<component /> element is actually an ASP.NET tag helper.
You can find out more about ASP.NET Tag Helpers in the official docs.
Tag helpers are able to employ the server to do various clever things before modifying the markup which gets returned to the browser.
In this case, the
component tag helper should do all the hard work of rendering HTML for our Blazor WASM app but at the moment it doesn’t appear to be doing very much at all!
To make it work add a
_ViewImports.cshtml file to your Server application’s Pages folder as follows:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Run this again now and hopefully you’ll find yourself looking at the oh so familiar Blazor project template.
What’s more, you should find as you interact with the app (for example by incrementing the count in the Counter component) everything works as it normally would, including clicking links to move between pages.
Success? Not so fast!
So it works, but before you go off to celebrate, check your browser console and you’ll discover a big red error.
Microsoft.JSInterop.JSException: Could not find any element matching selector ‘#app’.
So what gives?
Well your Blazor client application is expecting to load into an element with the
Essentially our use of the
<component> tag helper superceeds this but we forget to tell that to the .Client project!
Not to worry you can easily fix it by heading over to the Client project’s
In there, remove this line:
With that line removed the Blazor WASM client app will no longer attempt to load itself into an
#app element, leaving the
component tag helper to do its thing.
Eek, don’t refresh that page
At this point you might think everything is working perfectly.
You can navigate between different pages in your application, click on buttons and everything works!
However, if you navigate to the
FetchData page then hit refresh, you get an error!
InvalidOperationException: Cannot provide a value for property ‘Http’ on type ‘ExampleApp.Client.Pages.FetchData’. There is no registered service of type ‘System.Net.Http.HttpClient’.
My next post will explore this issue, explain what’s going wrong and offer a potential solution to the problem.
If you’re able to host your Blazor WASM projects via ASP.NET you can take advantage of prerendering improvements in .NET 5.
With a few tweaks you can render your app as static HTML so it loads nice and quickly in the user’s browser.
Once Blazor WASM has kicked in your app will become interactive and everything works just as if you’d loaded the Blazor app without prerendering.
- Part 2 - Solving the missing HTTP client problem
- Dan Roth’s .NET 5 samples
- Chris Sainty’s post on prerendering pre .NET 5 Blazor WASM applications