Why can't your Blazor WebAssembly PWA with offline support update even though you reload it?
Tags: blazor, pwa
Published: 2022-09-07 06:44:51

## 😥 Introduction

Please imagine the case that you have a Blazor WebAssembly PWA site that supports offline mode, and you've been opening that PWA by a web browser.
If you published a new version of that PWA, you might want to verify it, I guess. In this case, you will reload the page on that web browser.

However, you might **never see the contents of the new version** unexpectedly even though you hard-reload it. Disabling cache via the browser's developer tools window or clicking the "Empty cache and hard refresh" menu item from right-clicking the browser's reload icon button **doesn't have any effect at all**.😱

To enable the new version of that PWA, you must **close the browser's "all" tabs** that is opening that PWA site once. After closing them once and reopening that PWA site, you will see the contents of the new version of that PWA. 

## 🤔 This behavior is "by design"

This is by design come from web browser's specifications and from the default implementation of the service worker JavaScript that is generated by the project template of Visual Studio or .NET SDK.

First of I should explain is a service worker will be updated to a new one "atomically". When a web browser detects a new version of service worker JavaScript code, a web browser will load it but makes it remain in the "waiting to activate" status. This is a standard web platform behavior. A service worker is a process shared by all browser tabs and control caching mechanism offline support. Therefore if its update mechanism is not "atomically", it will cause critical confusion, such as different tabs of the same PWA different behave or the page contents of the current version being partially contaminated by the new versions' contents.

## ⚙️ Update to the next version without closing all tab

However, in some cases, you might need to make the PWA site able to update to the next version immediately without closing all tabs.

In this case, the better practice is notifying the next version is ready to users via its user interface. And if users respond to that UI to update to the next version, your service worker can be implemented to do that via [the `skipWaiting()` method](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting).

Unfortunately, the implementation will be complicated a little.

1. To be able to invoke the `skipWaiting()` method in the service worker process from outside of the service worker process, such as user interaction code, implement service worker JavaScript to handle the specific "message" and call the `skipWaiting()` method in that message handler.

2. Implement the user interface that will show a message such as "The next version is ready" and a button such as "Update now". And make that UI send the specific message to the service worker process to invoke the `skipWaiting()` method of the service worker process when users click the button.

3. Handle the promise callback of the `navigator.serviceWorker.register` method.

4. if there is a "waiting" service worker process (that is the new version), then notify the next version is ready to users via UI.

5. handle the `updatefound` event of the service worker registration object. If the `updatefound` event is fired, handle the `statechange` event of the service worker process.

6. If the `statechange` event of the service worker process is fired, then check the current status of that service worker process. If the status is "installed", then notify the next version is ready to users via UI.

7. If the status is "activated", that means users respond to the UI, and the `skipWaiting()` method in the service worker process is invoked. Therefore reload the current page by calling the `window.location.reload()` method.

...Please don't misunderstand me, but are you keeping up with this explanation? 😅

## 🎉 The "Blazor PWA Updater" NuGet package and component

Fortunately, you don't need to implement all of the code above by yourself. Instead, you can use the **"Blazor PWA Updater"** NuGet package and its component.

1. Install the `Toolbelt.Blazor.PWA.Updater` NuGet package.

2. Register a "PWA updater" service to a service provider at startup.

3. Place a `<PWAUpdater>` component somewhere in your Blazor PWA

4. Add the `"SKIP_WAITING"` message handler to the `service-worker.published.js` file and invoke the `skipWaiting()` in that message handler.

5. Replace the script element `<script>navigator.serviceWorker.register('service-worker.js');</script>` inside of the `index.html` to
`<script src="_content/Toolbelt.Blazor.PWA.Updater.Service/script.min.js"></script>`.

That's all. After the implementation above, you will get the ability on your Blazor PWA to update to the new version immediately via user interaction.

![](https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.PWA.Updater/main/.assets/movie.001.gif)

You can also customize the appearance of the `<PWAUpdater>`, such as notification text, button caption, text color, background color, sizes, and position.

If you don't prefer the appearance and behavior of the `<PWAUpdater>` at all, you can also implement the user interface from scratch with minimal effort.

The `IPWAUpdaterService` service lived in the `Toolbelt.Blazor.PWA.Updater.Service` NuGet package is exposing `NextVersionIsWaiting` event  and `SkipWaitingAsync()` method. So you can build your user interface with this service by yourself.

For more details, please visit the GitHub repository of the "Blazor PWA Updater" and read the README of it.

{% embed https://github.com/jsakamoto/Toolbelt.Blazor.PWA.Updater %}

## ⚠️ Notice

Please keep in your mind that **kind of these updating notifications sometimes makes users feel annoyed**, especially if it happens so often.

If you don't have enough reason to implement such a notification user interface to update the new version immediately, it will be better to keep it by the default implementation.

❤️_Happy coding :)_