Blazor Initializing State
02/16/2020
3 minutes
Problem
Using WebAssembly based Blazor is one great technology to create static websites using C# and mono. Unfortunately search engines are not yet good enough to index WebAssembly based websites. A way to overcome this issue is to create a Host service which only does pre-render and serve the files for the static website. This way search engines can load the pre-rendered HTML page, and index that.
When the webassembly based, client side, static page is fully loaded and initialized, the same state must be re-created as the pre-rendered page had during rendering. If initiating this state includes an async operation, such as fetching data from a service, the Blazor client might finish the first render before the required data is available. Without data, the first render will render an empty page. Once the state initialized a new render will re-create the same (or similar) DOM as the pre-rendered page had. Having these 2 renders results a huge 'flash' like experience for the user, where an empty page is rendered for a moment.
Proposal
There are several ways to solve this issue. Previously, one could capture the DOM before the first render of the client side Blazor happens, and just display that captured string as a markup string during the state initialization. A similar experience can be achieved by CSS styling too. A better solution though is provided since Blazor 3.2.0-preview1.20073.1, which allows initializing the state during startup of the client.
Solution
The new Blazor version creates the host and configures services in Program.cs (Startup.cs is not needed anymore). Building the host this way also enables to have an intermediate step between building the host and running it. Between the two operations, we can even run an async operation, which will execute before the first render. The sample below shows how to initialize the client side application this way. In the below example at ConfigureServices(builder.Services);
the Services property is an IServiceCollection
while host.Services
at initialization is IServiceProvider
enabling to resolve any service we registered in the previous step.
public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); ConfigureServices(builder.Services); var host = builder.Build(); //Initialize state by caching most recent posts await InitializeState(host.Services); await host.RunAsync(); }
Note, that this is great, though it does not handle all use-cases. If you have some page specific logic, or your data to be fetched depends on a query parameter of your page, you might not be able to pre-fetch that data during startup.