Blazor WASM PWA — Adding a “New Update Available” notification

Wouter Huysentruit
7 min readNov 30, 2021

In this article, I want to explain why and how we can add a “New Update Available” notification to a Blazor WebAssembly PWA project in some sort of step-by-step guide.

In this article, we will make use of .NET6 and the dotnet command-line, no specific IDE is required. If you don’t have .NET6 installed, please download it from here.

Execute dotnet --version at the command-line to figure out the .NET version that will be used. At the time of writing, this is 6.0.100.

Disclaimer

First of all, I want to point out that the information in this article only applies to Blazor WASM projects that have been configured with PWA functionality. Building a PWA is great as it allows your users to install your web application and work with it as if it was a native application, however make sure that you understand the caveats for offline PWAs before you proceed. Not sure what a PWA is? Read more about it here.

Transforming an existing project to PWA

An existing Blazor WASM project can be transformed to a PWA by adding some stuff. The easiest way is to create a new Blazor WASM PWA project and copy some files. The files you’re interested in are icon-*.png, manifest.json and service-worker*.js. After copying those files, open the generated index.html and copy over the relevant <link> and <script> tags that link to the manifest, icons and service-worker.

Why do we want a notification?

By default, when a users goes to your Blazor WASM SPA application, the service worker will show the offline/cached version of the application. When the server is available, the service worker will check if a new version of the application is available or not. It does this by downloading service-worker-assets.js which contains the hashes of all assets (generated during build) and compares those with the hashes of the cached files. Outdated files are being downloaded and a new service worker gets installed.

However, the user is still working with the old version of the application and is not aware that a new version has been downloaded and installed in the background. Newly installed versions only get activated when the user refreshes the page or reopens the browser. It would be nice if the user got a notification that a new version is available and can activate the new version with the click of a button. This is exactly what we will add throughout this article.

Let’s do it!

Create the project

Create a new Blazor WebAssmbly PWA project from the command-line:

dotnet new blazorwasm --pwa --name UpdateSample

If you also want to add some sort of server-side API to your project, you should also add the --hosted flag. However, for the simplicity of this article, we will only focus on the Blazor WASM client and thus will not generate a ASP.NET Core host.

Run the project:

cd UpdateSample
dotnet run

If you’ve created the project with an ASP.NET Core host (--hosted flag), you’ll have to execute dotnet run from the Server sub-directory instead.

Command-line output of dotnet run

Browse to the https URL as displayed in the output of dotnet run command and verify a page like this is displayed:

Default Blazor WASM Home page

In the URL-bar there’s also a small icon (3 squares with plus-sign) that allows you to install this web application. If you hover the icon, a caption like “App available. Install UpdateSample.” will appear. You don’t have to do this at this point but it indicates that our Blazor WASM project is correctly configured as a PWA.

In the next section, we will modify some files, so before doing that, terminate dotnet run by pressing Ctrl+C in the command-line.

Modify service-worker.js

When you open the UpdateSample project folder in the IDE of your choice, you will notice that there are two service-worker JavaScript files under the wwwroot sub-directory:service-worker.js and service-worker.published.js. The first file contains a dummy service worker that will forward all requests to the server, this is done to make sure that the application always downloads files directly from the server instead of using cached versions during development.

The real service-worker implementation is in the service-worker.published.js file, which is only used when we would do a dotnet publish . This makes it hard to test implementation changes we make to the service-worker, as we would need to publish and deploy our application each time we want to see the result of such change.

To make it easier to test changes to the service worker, without having to publish and deploy, we will copy the entire content of service-worker.published.js to service-worker.js . This enables us to test the real service worker implementation during development. However, make sure to keep a backup of the original content of service-worker.js, as you probably want to revert to it once the final implementation is ready.

After copying the content, add these lines right below console.info in the onInstall function:

// Activate the new service worker as soon as the old one is retired.
self.skipWaiting();

This line is required to force-activate a new service worker once it has been installed.

The entire service-worker.js file should look like this:

Add sw-registrator.js

Create a new file sw-registrator.js in the wwwroot sub-directory and copy/paste this content:

This file creates a global Promise updateAvailable that will be resolved whenever there is a new version available of the application. It also defines the method registerForUpdateAvailableNotification , that later will be invoked from the Blazor WASM client in order to register the C# callback on which the update-available notification needs to be received. The functionality of registerForUpdateAvailableNotification is pretty straightforward: it takes the name of a C# method that should be called once the promise updateAvailable gets resolved.

Modify index.html

Open index.html from the wwwroot sub-directory and replace the line:

<script>navigator.serviceWorker.register('service-worker.js');</script>

by this line:

<script src="sw-registrator.js"></script>

This change will make sure that the file we’ve created in the previous step is loaded and that the service worker is registered from sw-registrator.js instead of index.html .

Now the changes to the service worker itself are finished, it is time to adapt the Blazor side and let it show a message when a new version of the application is available.

Create Blazor component

We will create a new component called <UpdateAvailableDetector/> that will show a snackbar-like message when a new version of our application is available. To do this, we need to create a .razor and .razor.css file, the former will contain the Blazor component while the latter will contain some styling to make everything look good.

In the Shared folder, create a new file UpdateAvailableDetector.razor and copy this content into it:

Let’s quickly discuss what this component does: on initialize ( OnInitializeAsync ) the RegisterForUpdateAvailableNotification method gets called, which in turn will invoke the JavaScript function registerForUpdateAvailableNotification that we’ve created earlier. As we have seen, that JavaScript method registers the name of the C# method that needs to be called once an update is detected.

When the JavaScript code detects an update, the C# method OnUpdateAvailable will be invoked and the _newVersionAvailable will be set to true . We must inform Blazor of this state change by calling StateHasChanged() . This will force the component to re-render, and this time the <button> will be shown because _newVersionAvailable is true.

Now, we’re going to style the component. Create a second file UpdateAvailableDetector.razor.css and paste these styles into it:

This will make sure the update button is shown bottom right and will be made visible with a little sliding animation. Because, why not 😋

Modify MainLayout.razor

Once our component is finished, we need to include it somewhere. A good place to do this is the MainLayout.razor component, because that component mostly stays on-screen for the entire lifetime of the application. If your application uses different layouts, you can consider moving the component to App.razor f.e.

Open MainLayout.razor and add our component at the end:

<UpdateAvailableDetector/>

And that’s it. Now it’s time to test our feature.

Testing the notification

If you change something, f.e. to Index.razor, re-run the application with dotnet run and refresh the browser using F5, you should see the notification come up like this:

Screenshot of update notification

Once you click the notification, the app should refresh and show you the latest version.

During my tests, I have seen that dotnet run doesn’t regenerate the hashes, so after each change you make, it’s best to run dotnet build before dotnet run .

Detect while the application is running

As you might notice, the update notification will only come up when you refresh the browser or when you arrive at the application after a new version has been built/deployed. The notification will not show up if the deploy happens while the application is in use.

With a small modification, we can let the service-worker periodically check for updates and receive a notification by leveraging the implementation that we already had in place. To do so, we need to start an interval timer once the service worker has been registered. This is right after the console.info(`Service worker registration successful (scope: ${registration.scope})`); line in sw-registrator.js :

setInterval(() => {
registration.update();
}, 60 * 1000); // 60000ms -> check each minute

Wrapping up

Well, there’s nothing more to add. I hope this post was of any use to you. If you’re stuck with something, please reach out to me on Twitter.

Happy coding!

--

--

Wouter Huysentruit

Software Engineer and Architect focusing on .NET and Microsoft technologies. Microsoft MVP. Practitioner of clean code. #solid #tdd #ddd #cqrs #es #graphql