This codelab shows you, step-by-step, how to build a push notification client. By the end of the codelab you'll have a client that:
- Subscribes the user to push notifications.
- Receives push messages and displays them as notifications.
- Unsubscribes the user from push notifications.
This codelab is focused on helping you learn by doing and doesn't talk about concepts much. Check out How do push notifications work? to learn about push notification concepts.
The server code of this codelab is already complete. You'll only be implementing the client in this codelab. To learn how to implement a push notification server, check out Codelab: Build a push notification server.
Check out push-notifications-client-codelab-complete (source) to see the complete code.
Browser compatibility
This codelab is known to work with the following operating system and browser combinations:
- Windows: Chrome, Edge
- macOS: Chrome, Firefox
- Android: Chrome, Firefox
This codelab is known to not work with the following operating systems (or operating system and browser combinations):
- macOS: Brave, Edge, Safari
- iOS
Setup
Get an editable copy of the code
The code editor that you see to the right of these instructions will be called the Glitch UI throughout this codelab.
- Click Remix to Edit to make the project editable.
Set up authentication
Before you can get push notifications working, you need to set up your server and client with authentication keys. See Sign your web push protocol requests to learn why.
- In the Glitch UI click Tools and then click Terminal to open the Glitch Terminal.
- In the Glitch Terminal, run
npx web-push generate-vapid-keys
. Copy the private key and public key values. - In the Glitch UI open
.env
and updateVAPID_PUBLIC_KEY
andVAPID_PRIVATE_KEY
. SetVAPID_SUBJECT
tomailto:test@test.test
. All of these values should be wrapped in double quotes. After making your updates, your.env
file should look similar to this:
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
- Close the Glitch Terminal.
- Open
public/index.js
. - Replace
VAPID_PUBLIC_KEY_VALUE_HERE
with the value of your public key.
Register a service worker
Your client will eventually need a service worker to receive and display notifications. It's best to register the service worker as early as possible. See Receive and display the pushed messages as notifications for more context.
- Replace the
// TODO add startup logic here
comment with the following code:
// TODO add startup logic here
if ('serviceWorker' in navigator && 'PushManager' in window) {
navigator.serviceWorker.register('./service-worker.js').then(serviceWorkerRegistration => {
console.info('Service worker was registered.');
console.info({serviceWorkerRegistration});
}).catch(error => {
console.error('An error occurred while registering the service worker.');
console.error(error);
});
subscribeButton.disabled = false;
} else {
console.error('Browser does not support service workers or push messages.');
}
subscribeButton.addEventListener('click', subscribeButtonHandler);
unsubscribeButton.addEventListener('click', unsubscribeButtonHandler);
- To preview the site, press View App. Then press Fullscreen .
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
- Click the Console tab. You should see the message
Service worker was registered.
logged to the Console.
Request push notification permission
You should never request permission to send push notifications on page load. Instead, your UI should ask the user if they want to receive push notifications. Once they explicitly confirm (with a button click, for example) then you can start the formal process for getting push notification permission from the browser.
- In the Glitch UI click View Source to return to your code.
- In
public/index.js
replace the// TODO
comment insubscribeButtonHandler()
with the following code:
// TODO
// Prevent the user from clicking the subscribe button multiple times.
subscribeButton.disabled = true;
const result = await Notification.requestPermission();
if (result === 'denied') {
console.error('The user explicitly denied the permission request.');
return;
}
if (result === 'granted') {
console.info('The user accepted the permission request.');
}
- Go back to the app tab and click Subscribe to push. Your browser or operating system will probably ask you if you want to let the website send you push notifications. Click Allow (or whatever equivalent phrase your browser/OS uses). In the Console you should see a message indicating whether the request was accepted or denied.
Subscribe to push notifications
The subscription process involves interacting with a web service controlled by the browser vendor that's called a push service. Once you get the push notification subscription information you need to send it to a server and have the server store it in a database long-term. See Subscribe the client to push notifications for more context about the subscription process.
- Add the following highlighted code to
subscribeButtonHandler()
:
subscribeButton.disabled = true;
const result = await Notification.requestPermission();
if (result === 'denied') {
console.error('The user explicitly denied the permission request.');
return;
}
if (result === 'granted') {
console.info('The user accepted the permission request.');
}
const registration = await navigator.serviceWorker.getRegistration();
const subscribed = await registration.pushManager.getSubscription();
if (subscribed) {
console.info('User is already subscribed.');
notifyMeButton.disabled = false;
unsubscribeButton.disabled = false;
return;
}
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY)
});
notifyMeButton.disabled = false;
fetch('/add-subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
});
The userVisibleOnly
option must be true
. It may one day be possible
to push messages without displaying user-visible notifications
(silent pushes) but browsers currently don't allow that capability
because of privacy concerns.
The applicationServerKey
value relies on a utility function that
converts a base64 string to a Uint8Array. This value is used for
authentication between your server and the push service.
Unsubscribe from push notifications
After a user has subscribed to push notifications, your UI needs to provide a way to unsubscribe in case the user changes their mind and no longer wants to receive push notifications.
- Replace the
// TODO
comment inunsubscribeButtonHandler()
with the following code:
// TODO
const registration = await navigator.serviceWorker.getRegistration();
const subscription = await registration.pushManager.getSubscription();
fetch('/remove-subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({endpoint: subscription.endpoint})
});
const unsubscribed = await subscription.unsubscribe();
if (unsubscribed) {
console.info('Successfully unsubscribed from push notifications.');
unsubscribeButton.disabled = true;
subscribeButton.disabled = false;
notifyMeButton.disabled = true;
}
Receive a push message and display it as a notification
As mentioned before, you need a service worker to handle the receiving and displaying of messages that were pushed to the client from your server. See Receive and display the pushed messages as notifications for more detail.
- Open
public/service-worker.js
and replace the// TODO
comment in the service worker'spush
event handler with the following code:
// TODO
let data = event.data.json();
const image = 'https://cdn.glitch.com/614286c9-b4fc-4303-a6a9-a4cef0601b74%2Flogo.png?v=1605150951230';
const options = {
body: data.options.body,
icon: image
}
self.registration.showNotification(
data.title,
options
);
- Go back to the app tab.
- Click Notify me. You should receive a push notification.
- Try opening the URL of your app tab on other browsers (or even other devices), going through the subscription workflow, and then clicking Notify all. You should receive the same push notification on all of the browsers that you subscribed. Refer back to Browser compatibility to see a list of browser/OS combinations that are known to work or not work.
You can customize the notification in lots of ways. See the parameters of
ServiceWorkerRegistration.showNotification()
to learn more.
Open a URL when a user clicks a notification
In the real-world, you'll probably use the notification as a way to re-engage your user and prompt them to visit your site. To do that, you need to configure your service worker a bit more.
- Replace the
// TODO
comment in the service worker'snotificationclick
event handler with the following code:
// TODO
event.notification.close();
event.waitUntil(self.clients.openWindow('https://web.dev'));
- Go back to the app tab, send yourself another notification, and then
click the notification. Your browser should open a new tab and load
https://web.dev
.
Next steps
- Look at
ServiceWorkerRegistration.showNotification()
to discover all of the different ways you can customize notifications. - Read Push notifications overview for a deeper conceptual understanding of how push notifications work.
- Check out Codelab: Build a push notification server to learn how to build a server that manages subscriptions and sends web push protocol requests.
- Try out Notification Generator to test out all the ways you can customize notifications.