DEV Community

Cover image for #14 - Notifying Users Of Updates
Nitya Narasimhan, Ph.D for Microsoft Azure

Posted on • Originally published at microsoft.github.io

#14 - Notifying Users Of Updates

Welcome to Day 14 of #30DaysOfPWA! New to the series? Three things you can do to catch up:

This is a shortened version of this canonical post for the #30DaysOfPWA.


About The Author

Today's post is authored by Maxim Salnikov - a member of the Microsoft Norway Developer Relations team. Follow Maxim at @webmaxru or here on dev.to.


Welcome to day week 2 day 7 of the 30 Days of PWA series! Today's blog post will introduce various ways to keep users notified and re-engaged with your application.


What are notifications?

A notification, in the context of a web application, is a way to re-engage users. Often they are used to alert users to something that's happening in the app’s backend and/or within the app itself. For example:

  • A new message in a chat room
  • A new version of the app is available
  • Your flight is ready for check-in

Various kinds of notifications, based on their severity and urgency, require different kinds of user interaction: from blocking UI until user reaction on some request to no interaction at all, just informing in some prominent or subtle way. As a developer, you choose the exact type of notification based on your application business logic to provide the best user experience.

Notification arrival time is not always predictable (at least from the user's point of view). What if your web application is not open in the user's browser when you sent an update? You have to have some part of the application always "on duty" in the background to listen to the events from your server and potentially show a notification. For sure, only a service worker can handle this scenario.

In this chapter, let's go through some interesting possibilities of keeping users informed, using the power of Progressive Web App APIs.


Application badges

We’ll start with the most subtle way to notify users that "something new" is available - a badge over the app icon. As mobile app users, we are familiar with the concept of badges. They are small graphical elements that are placed over the app icon on the home screen, taskbar, etc. The exact badge appearance depends on the specific platform. Often, this is a circle with contrasting background color containing the number of new items. For example, the number of unread messages in a mailbox.

It's possible to use the same mechanism for web applications, using the Badging API. Of course, since this applies to the application icon, we’re talking specifically about installed web applications (i.e. with offline support via service worker and correct web app manifest) - check "Make PWA Installable" and "Make PWA Work Offline" sections to learn more about it.

How to start

To manage a badge over the app icon, there are two methods available: setAppBadge() and clearAppBadge(). Let's assume we want to inform our user about 3 new messages in the mailbox:

// Check if the API is supported
if ('setAppBadge' in navigator) {
    navigator.setAppBadge(2).catch((error) => {
        // Code to handle an error
    });
}
Enter fullscreen mode Exit fullscreen mode

To remove a badge:

navigator.clearAppBadge()
Enter fullscreen mode Exit fullscreen mode

or

navigator.setAppBadge(0)
Enter fullscreen mode Exit fullscreen mode

This is what an app icon with a badge looks like on the Windows 11 taskbar:

App badge in Windows taskbar

Here, an icon with a blue badge overlay is a PWA.

You can manage the badge both from your main application and from the service worker. The latter provides a very important use-case of setting the badge when the application tab (or even visible part of the browser) is not open. What are the most relevant events to listen to in your service worker for managing badges? Currently, there are two of them with their pros and cons:

  • "push" - when a push notification is received. Pros: we control the exact timing of sending the notification request. Cons: all requirements and limitations of Push API (see details below). Also, it's obligatory to show a browser-native notification on push, so we can’t issue a "silent" badge update.
  • "periodicsync" - an event from Periodic Background Sync API we covered in "Synchronizing app in the background" section. Pros: there are no other actions or notifications needed, the badge update can happen in the background. Cons: all requirements and limitations of the Periodic Background Sync API. One of them is that there is no way to update the badge at a specific time - it's the browser that decides when to fire this event for a particular installed application, based on the Site Engagement index. And it's going to be, at best, several hours between syncs.

As you see, currently, there is no perfect way to manage the badge from the service worker. The discussion about this topic is ongoing, and there are some proposals about making Push API work better with badging.


Push notifications on the web

Another well-known mobile platform feature that came to the web — and is relevant to the task of keeping users updated — is push notifications. They are a way to send a packet of data (that might consist of a title, short description, url, image, several other kinds of data) to the user’s device from your backend. Often, push notifications are explained as a possibility for the app owners to re-engage users. Some distinctive features of push notifications are:

  • They always use OS- or browser-native UI controls to display the notification. For the web, this is Notifications API. It provides the best possible developer and user experience.
  • Users explicitly opt-in to receive push notifications and can opt-out at any time. If the app developer doesn't provide the latter option in the UI, users can use platform native tools to block push notifications for the app (on mobile platforms) or for the origin (on the web).
  • The user device can receive and show these notifications, regardless of whether the app is open or not. Thus, for the web platform, we listen and react to the corresponding event in a service worker.
  • There is always a 3rd party service that handles subscriptions and sends push notifications to the user devices. It's called "Messaging Service" and it's not a part of either the operating system or browser. A Messaging Service is a powerful server and network infrastructure able to deliver messages directly to the devices on a huge scale. As a developer, you can't (and don't need to) choose a specific Messaging Service - you implicitly use it in your code that works with Push API during subscription, unsubscription, sending a notification. And it's free to use.

How to start

Implementing push notifications on the web includes multiple steps:

Step 1. Getting credentials.

Generate a set of "VAPID keys" to authenticate a user device against the Messaging Service. You need one set of keys (public and private) per app. You can use either web-push module or one of the many free online services. Using web-push:

npm install web-push -g
web-push generate-vapid-keys --json
Enter fullscreen mode Exit fullscreen mode

Step 2. Subscribing and saving.

After the user explicitly opts-in to receive push notifications (for example, by clicking a button in the UI), use the subscribe() method of the PushManager interface (if it's available) of the active service worker registration:

It is a best practice to either hide or disable all related UI controls if there is no Push API support in the browser:

if (!('PushManager' in window)) {

    // Code to disable or hide push-related UI controls

    console.log('Push API is not supported');
    return;
}
Enter fullscreen mode Exit fullscreen mode

"Subscribe" button handler (for the sake of the example, the code is simplified and doesn't contain error handling):

async function subscribeForPush() {
    const registration = await navigator.serviceWorker.ready;

    const pushSubscription = await registration.pushManager.subscribe({
        userVisibleOnly: true, // Should be always true as currently browsers only support explicitly visible notifications
        applicationServerKey: "publicKey from Step 1 converted to Uint8Array"
    });

    // Send push subscription to our server to persist it
    saveSubscription(pushSubscription);
}
Enter fullscreen mode Exit fullscreen mode

With that, the subscription process is completed. From here, there are several options to improve the user experience:

  • You can request subscription status from the Messaging Service by using getSubscription() to set the initial status for subscription UI controls.
  • Before subscribing, it's a good idea to check/request permission for the notification using Notifications API. That gives you more control over when the permission prompt is shown. If you skip that step, calling subscribe() will prompt the user to grant/deny the notification permission.
  • Don't forget to implement unsubscription logic and place corresponding UI controls. It's always better to let users unsubscribe using your code (they can resubscribe again in your app) than to make them block your origin using browser settings (which would require them to unblock your origin in the browser settings again if they want to resubscribe).

You will find an extended code sample that also includes method for converting the publicKey to Uint8Array here.

Step 3. Sending a notification.

In your backend, iterate through the push subscription objects you received and saved in Step 2 to send a special HTTPS request to the Messaging Service for each of them. Good to remember: push notifications on the web are not limited to broadcast messages. You can send a specific notification to a specific user device if together with the push subscription object you pass some user-identifying data (like your internal user ID) to the backend, so you can iterate over only specific subscriptions.

To build a request that follows Web Push protocol, you use:

  • endpoint field from the subscription object that is actually a URL of the Messaging Service with a unique device token
  • VAPID keys you generated in Step 1
  • The payload you want to send to the user device for this particular notification. Often, you JSON.stringify() the object following Notification specification.

The simpler way to send a notification from your NodeJS backend is using sendNotification() method from the web-push module. There are similar libraries for other platforms available.

The Messaging Service receives your request and sends a push notification to the user.

Step 4. Receiving and displaying.

To receive a notification, a service worker on the user device listens for the push event and handles it:

self.addEventListener('push', (event) => {
    const notificationData = JSON.parse(event.data.text());

    event.waitUntil(
        self.registration.showNotification(notificationData.title, {
            body: notificationData.message,
            icon: notificationData.icon
        });
    );
});
Enter fullscreen mode Exit fullscreen mode

To react to the user actions, you can also provide handlers for the notificationclick and notificationclose events in the service worker.

Because of the web specifics, push notifications there have some extra interesting points:

  • Showing notification with data sent as a payload from your server (proxied by the Messaging Service) is only one possible scenario. Before displaying the notification, you can, for example, do another API call to request additional data. Of course, you have to consider limited service worker execution time. So all that happens in your code before calling showNotification() should not be too long.
  • In a service worker, on receiving a push event, you MUST use showNotification() to show a native notification to the user (with only one exception). If this method is not called or called incorrectly, the notification will still be shown informing the user about "something happened with your app in the background" (the exact text is decided by the browser). This is done to prevent malicious apps from "waking up" service workers by a signal from the backend without letting the user know about it.
  • On Windows, push notifications are integrated into the Action Center.

A push notification in the Windows Action Center

On other operating systems, they might be also integrated into centralized notification UI or look like standalone browser-native dialogs.

There is a web-push demo with the source code available that you can use to explore the notification functionality.


Learn more about notifying your users of updates


Next Week: Developer Tools!

We are about to cross the half-way mark of our #30DaysOfPWA journey. By now, you should have a solid foundation of core concepts, and have hands-on experience working with advanced capabilities.

Here's a sneak peek at what's in store for Week 3 - get ready to dive into developer tools to jumpstart your PWA, debug it, audit it for performance & accessibility, integrate testing and automation workflows, and package it to publish in app stores.

Visual Guide to Week 3 Roadmap


Want to read more content from Microsoft technologists? Don't forget to follow Azure right here on dev.to:


Top comments (0)