Web Push Notification

Arie M. Prasetyo
7 min readJun 4, 2021

What are Web Push Notifications?

Web Push Notifications or browser notifications are notifications that website visitors can opt-in to receive to stay updated without installing an app. These clickable rich-content messages can be sent to users’ devices by a website/web app.

Browser support

It is important to note that iOS does not support web push notifications yet. On mobile browsers, the support is limited to Android devices only (Chrome, Firefox, Opera).

On the desktop, web push notifications are supported by Chrome, Firefox, Safari, Opera, and Edge.

Service worker

Push notification is handled by the browser’s service worker. A service worker receives push event when the it arrives on the device.

We can use the service worker to subscribe to notifications. The Push Manager interface of the Push API provides a way to receive notifications from third-party servers, as well as request URLs for push notifications.

We can check whether a browser supports service worker and/or Push Manager, with these lines of code:

if (!('serviceWorker' in navigator)) {
// Service Worker isn't supported on this browser,
// disable or hide UI.
return;
}

if (!('PushManager' in window)) {
// Push isn't supported on this browser, disable or hide UI.
return;
}

Elements of web push notification

There are 6 key elements that constitute a web push notification:

  • title,
  • description,
  • landing page URL,
  • icon,
  • banner image, and
  • call-to-action buttons

You can also make use of emojis in web push notifications.

How push works

The three key steps to implementing push are:

1. Subscribe a user to push notification

Below is an example on how to add a client side logic that subscribes a user to push notification (i.e. the JavaScript and UI in your web app that registers a user to push messages).

Register a service worker

First we need to register a service worker* to handle our subscription to push notification.

*more information on the service worker file on step 3

const register = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});

We’ve registered our service worker and are ready to subscribe the user. Once we have our service worker registered and we’ve got permission, we can subscribe a user by using the PushManager.

Create a subscription

On the client side, the first step is to “subscribe” a user to push messaging. Subscribing a user requires two things:

First, getting permission from the user to send them push messages. The browser will show a popup that asks user to allow the website to receive & show push notifications.

Second, getting a push subscription object from the browser. It contains all the information we need to send a push message to that user. We can “kind of” think of this as an ID for that user’s device.

We create push subscription object using the PushManager.

// Create a subscription object to send to the serverconst subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY),
});

How to get application server keys (VAPID keys)

Before subscribing a user we’ll need to generate a set of “application server keys”. The application server keys, also known as VAPID keys, are unique to our server. They allow a push service to know which application server subscribed a user and ensure that it’s the same server triggering the push messages to that user.

Application server keys are a public and private key pair that are unique to your application. The private key should be kept a secret to your application and the public key can be shared freely.

Once we’ve subscribed the user and have a subscription object, we’ll need to send the subscription details to our backend/server. On our server, we can save this subscription to a database and use it to send a push message to that user.

You can create a public and private set of application server keys by visiting:
Push Companion

Or you can use the web-push library’s command line to generate keys:

$ npm install -g web-push
$ web-push generate-vapid-keys

2. Trigger push message

The API call from your back-end/application that triggers a push message to a user’s device.

This in example of codes in a Node JS server that receives the subscription object from the client:

const webPush = require('web-push');/**
* push the notification
* use the subscription information received from the client,
* back to the client, to trigger the service worker
*/
webPush
.sendNotification(PUSH_SUBSCRIPTION, PAYLOAD)
.catch(error => console.error(error));

Reference on notification triggers

3. Service worker receives “push”

When a message is received, it’ll result in a push event being dispatched in the service worker. To handle this, we are going to need a service worker file.

The service worker is still just JavaScript. But the browser will “give it access” to the service worker APIs, including push. To be more exact, the browser runs the file (in this example: sw.js) in a service worker environment.

The service worker file will receive a push event when it arrives on the device. It’s in this example, we’ll make it show a notification. Here is an example of a service worker file that listens to a push notification event:

// sw.jsself.addEventListener('push', event => {
const data = event.data.json();
// visual options of the browser notification
const options = {
body: data.message,
icon: '/notification_image.png'
};
/**
* when this registered & subscribed service worker
* receives a "push" event,

* show the browser notification popup,
* using the data sent from server's webPush.sendNotification.

*/
self.registration.showNotification(data.title, options);
});

In the example above, self.addEventListener() can be thought of as adding an event listener to the service worker itself. Remember that we send a subscription to the server and it returns the subscription back using webPush.sendNotification(). That’s why this event listener is like “listening to itself”.

Inside the push event example, we check if there is any data and show the browsers notification UI.

More information on notification visual options

What is a service worker?

A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction. Today, they already include features like push notifications and background sync. In the future, service workers might support other things like periodic sync or geofencing. The core feature discussed in this tutorial is the ability to intercept and handle network requests, including programmatically managing a cache of responses.

A service worker is a “special” JavaScript file. The browser can execute this JavaScript without your page being open. It can even execute this JavaScript when the browser is closed.

Service worker lifecycle

A service worker has a lifecycle that is completely separate from your web page. To install a service worker for your site, you need to register it, which you do in your page’s JavaScript. Registering a service worker will cause the browser to start the service worker install step in the background.

Typically during the install step, you’ll want to cache some static assets. If all the files are cached successfully, then the service worker becomes installed. If any of the files fail to download and cache, then the install step will fail and the service worker won’t activate (i.e. won’t be installed). If that happens, don’t worry, it’ll try again next time. But that means if it does install, you know you’ve got those static assets in the cache.

How service worker works

To register a service worker, call navigator.serviceWorker.register(), passing in the path to our file. This function tells the browser that we have a service worker file and where it’s located. In this case, the service worker file is sw.js.

Behind the scenes, the browser will take the following steps after calling register():

  1. Download the service worker file.
  2. Run the JavaScript in the service worker file.
  3. If everything runs correctly and there are no errors, the promise returned by register() will resolve. If there are errors of any kind, the promise will reject.

Visual options for browser push notification

The API for showing a notification that we put in the service worker file is simply:

<ServiceWorkerRegistration>.showNotification({title}, {options});

Where the title is a string and options can be any of the following:

{
"//": "Visual Options",
"body": "<String>",
"icon": "<URL String>",
"image": "<URL String>",
"badge": "<URL String>",
"vibrate": "<Array of Integers>",
"sound": "<URL String>",
"dir": "<String of 'auto' | 'ltr' | 'rtl'>",

"//": "Behavioral Options",
"tag": "<String>",
"data": "<Anything>",
"requireInteraction": "<boolean>",
"renotify": "<Boolean>",
"silent": "<Boolean>",

"//": "Both visual & behavioral options",
"actions": "<Array of Strings>",

"//": "Information Option. No visual affect.",
"timestamp": "<Long>"
}

Example files

I’ve prepared an example with three components:

Node JS server

The server will handle notification request sent from the client (with a subscription object in the payload). And it will then push the notification, sending a subscription object back to the client.

Basic HTML/JS client

A simple HTML and JavaScript that requests and listens to push notifications.

React app client

A simple React application that requests and listens to push notifications. This example React app is hosted on a different port, so the server needs to allow CORS. And the webpack configuration needs to make the sw.js file publicly available.

As always, if you have any trouble or question, mention me on Twitter. Happy coding!

--

--