Blazor WASM PWA — Adding a “New Update Available” notification
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 theServer
sub-directory instead.
Browse to the https URL as displayed in the output of dotnet run
command and verify a page like this is displayed:
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:
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 rundotnet build
beforedotnet 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!