Render Blazor WASM components in your existing MVC/Razor Pages applications
October 13, 2020 · 4 minute read · Tags: blazor | prerendering
My recent post shows how to prerender a Blazor WASM application so your users aren’t left hanging while they wait for the application to load.
This is great for speeding up your Blazor WASM app’s initial load time but .NET 5’s new render modes also open up another compelling door.
You can prerender individual Blazor WASM components on any of your Razor pages (or views).
If you have an established application you may well want to start by incrementally migrating parts of the UI to Razor components (or adding new features as components while you find your Blazor feet)
The key to unlocking this is the component
tag helper and a new render mode called WebAssemblyPrerendered
.
Say you have a standard Razor Pages application and a Blazor WASM project, you can add a reference from the Razor Pages app to the Blazor WASM project, then include a component on any given Razor Page.
Index.cshtml
@using ExampleApp.Client.Pages
<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered"/>
<script src="_framework/blazor.webassembly.js"></script>
ExampleApp.Client
refers to the Blazor WASM project in this scenario.
WebAssemblyPrerendered
is a new render mode, included in .NET 5 RC1 and later, which effectively says this component should be rendered on the server and returned as static HTML.
It means there’s virtually no delay when someone access this page and they’ll see the component (counter in this case) immediately.
The component won’t be ‘interactive’ right away, but will spring into life once the browser finishes downloading and/or loading any files it needs (.NET runtime, your app’s dlls etc).
Here it is in action:
Render individual components - step by step
Here’s the exact process to make this work.
First, create a new (or use an existing) ASP.NET Core Razor Pages application.
Add this PackageReference
(to the Razor Pages app).
dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Server --prerelease
The --prerelease
flag makes sure you get the latest RC of .NET 5 (Release Candidate 1 at the time of writing).
Head over to Startup.cs
and add a call to app.UseBlazorFrameworkFiles()
(in the Configure
method)
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// other code
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles(); // add this
app.UseStaticFiles();
// other code
}
With that done, all that’s left is to reference the blazor WASM javascript file and the component you wish to render.
Index.cshtml (or any other Page/View)
@using ExampleApp.Client.Pages
<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered"/>
<script src="_framework/blazor.webassembly.js"></script>
At this point you’ll need to reference the Blazor project from your Razor Pages project.
If you plan on using this a lot it might also be worth moving that <script>
reference over to your shared layout page so you don’t have to add it to every individual page/view.
Gotcha - your WASM app expects an #app div
When you try this you will almost certainly get an error along these lines in the browser console:
blazor.webassembly.js:1 Microsoft.JSInterop.JSException: Could not find any element matching selector ‘#app’
This is because your Blazor WASM app expects to load itself into an #app
element and complains when it can’t find one.
You can prevent this from happening if you head over to the client (Blazor WASM) project, and remove this line from Program.cs
.
builder.RootComponents.Add<App>("#app");
With that removed Blazor will no longer attempt to render your entire app using the #app
div, leaving you free to render the components you wish via your Razor Pages.
OnInitializedAsync is called twice
One thing to watch here, as I outlined in my previous article on prerendering is that your component is rendered twice when prerendering; once on the server, to generate static HTML which is returned to the browser, then again on the client (in the browser) to actually load up your component and make it ‘dynamic’.
Consequently your OnInitialized
override (or its async equivalent) will be invoked twice.
Microsoft are aware of this issue and are exploring options. In the meantime, check out my previous articles for suggestions on how to mitigate the effects of this.
In summary
It’s much easier with .NET 5 to start using Blazor WASM components in your existing applications.
This opens the door to incrementally migrating towards Blazor WASM. You can either migrate existing logic/views, or opt to use Razor components for new features (where it makes sense to do so).
For the most part prerendering WASM components makes this fairly seamless for your users but there is a niggling issue with OnInitialized
being invoked twice at the moment which causes your UI to ‘flicker’ when the second render takes place.
Hopefully this is a short term issue and solutions (or workarounds) will be found in due course.
All posts in the
To .NET 5 and beyond series.
- Update the HTML head from your Blazor components
- From site.css to component styles
- Prerendering your Blazor WASM application with .NET 5 (part 1)
- Prerendering your Blazor WASM application with .NET 5 (part 2 - solving the missing HttpClient problem)
- Render Blazor WASM components in your existing MVC/Razor Pages applications
- Does .NET 6 fix Blazor Prerendering?