Push Notifications — Re-Engaging Users with Server Updates
In this tutorial, you will learn about Push Notifications. We cover key concepts, practical examples, and best practices to help you master this topic.
Push notifications send server updates to users even when the PWA is closed, using the Push API and service workers to display timely messages and re-engage users.
What You'll Learn
By the end of this tutorial, you will understand how push notifications work, how to subscribe users, how to send push messages from a server, and how to display notifications in the service worker.
Why It Matters
Notifications are the primary re-engagement channel for PWAs. Unlike native apps, PWAs can send notifications without requiring an app store installation. Push notifications increase return Visitor rates by 200-400% when used effectively.
Real-World Use
A news PWA sends push notifications for breaking stories. Users subscribe during onboarding. When a major story breaks, the server sends a push message with the headline. The user taps the notification and opens the article directly — even if they had closed the browser entirely.
Push Notification Architecture
Push Notification Flow
┌──────────┐ ┌───────────┐ ┌──────────┐
│ Browser │ │ Push │ │ Your │
│ (PWA) │ │ Service │ │ Server │
└────┬─────┘ └─────┬─────┘ └────┬─────┘
│ │ │
│ 1. Register SW │ │
│────────────────────│ │
│ │ │
│ 2. Subscribe │ │
│ to push │ │
│───────────────────>│ │
│ │ │
│ Return │ │
│ subscription │ │
│<───────────────────│ │
│ │ │
│ 3. Send sub to │ │
│ your server │ │
│────────────────────────────────────────>│
│ │ │
│ │ 4. Push message │
│ │<───────────────────│
│ │ │
│ 5. Service worker │ │
│ receives event │ │
│<───────────────────│ │
│ │ │
│ 6. Show │ │
│ notification │ │
│ │ │
Think of push notifications like a postal service. Your server writes a letter (push message), sends it through the post office (push service), and the Postman delivers it to your house (service worker). Even if you are not home (browser closed), the letter waits for you.
Subscribing to Push
// In your page JavaScript
async function subscribeToPush() {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
console.log('Push notifications not supported');
return null;
}
try {
const registration = await navigator.serviceWorker.register('/sw.js');
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9SmkvMl3fA94MtBwM5mPwVvM9AA'
)
});
console.log('Push subscription:', JSON.stringify(subscription));
return subscription;
} catch (error) {
console.error('Push subscription failed:', error);
throw error;
}
}
// Helper: Convert VAPID key
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; i++) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
Output:
Push subscription: {"endpoint":"https://fcm.googleapis.com/...","keys":{...}}
Service Worker Push Event
// sw.js — Handle push events
self.addEventListener('push', event => {
console.log('Push received:', event);
let data = {
title: 'Default Title',
body: 'You have a new notification',
icon: '/icons/icon-192.png',
badge: '/icons/badge.png',
url: '/'
};
// Parse the push data
if (event.data) {
try {
const payload = event.data.json();
data = { ...data, ...payload };
console.log('Push data:', payload);
} catch (e) {
console.log('Push data is text:', event.data.text());
data.body = event.data.text();
}
}
// Show the notification
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: data.icon,
badge: data.badge,
data: {
url: data.url,
timestamp: Date.now()
},
actions: [
{ action: 'open', title: 'Open' },
{ action: 'dismiss', title: 'Dismiss' }
],
vibrate: [200, 100, 200],
requireInteraction: true
})
);
});
// Handle notification click
self.addEventListener('notificationclick', event => {
console.log('Notification clicked:', event.action);
event.notification.close();
if (event.action === 'dismiss') return;
// Open or focus the app
const urlToOpen = event.notification.data.url || '/';
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then(windowClients => {
// If app is already open, focus it
for (const client of windowClients) {
if (client.url === urlToOpen && 'focus' in client) {
return client.focus();
}
}
// Otherwise open new window
if (clients.openWindow) {
return clients.openWindow(urlToOpen);
}
})
);
});
Sending Push Messages (Server-Side)
Node.js example using web-push:
// server.js — Send push notifications
const webpush = require('web-push');
// VAPID keys (generate once)
const vapidKeys = {
publicKey: 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9SmkvMl3fA94MtBwM5mPwVvM9AA',
privateKey: 'YOUR_PRIVATE_KEY_HERE'
};
webpush.setVapidDetails(
'mailto:admin@example.com',
vapidKeys.publicKey,
vapidKeys.privateKey
);
// Send notification to a subscriber
function sendNotification(subscription, payload) {
webpush.sendNotification(subscription, JSON.stringify(payload))
.then(result => {
console.log('Notification sent:', result.statusCode);
})
.catch(error => {
console.error('Send failed:', error);
// 410 Gone: subscription expired
if (error.statusCode === 410) {
console.log('Removing expired subscription');
removeSubscriptionFromDB(subscription.endpoint);
}
});
}
// Example: notify about new article
const subscription = {
endpoint: 'https://fcm.googleapis.com/...',
keys: {
auth: '...',
pkey: '...'
}
};
sendNotification(subscription, {
title: 'New Article Published',
body: 'Check out our latest guide on PWAs',
icon: '/icons/icon-192.png',
badge: '/icons/badge.png',
url: '/articles/pwa-guide',
tag: 'new-article'
});
Managing Subscription Status
// Check if already subscribed
async function checkSubscription() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();
if (subscription) {
console.log('Already subscribed:', subscription.endpoint);
return subscription;
}
console.log('Not subscribed');
return null;
}
// Unsubscribe
async function unsubscribe() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();
if (subscription) {
const success = await subscription.unsubscribe();
if (success) {
console.log('Unsubscribed successfully');
// Notify server
await fetch('/api/unsubscribe', {
method: 'POST',
body: JSON.stringify({ endpoint: subscription.endpoint }),
headers: { 'Content-Type': 'application/json' }
});
}
}
}
Common Mistakes
- Not setting userVisibleOnly: true. Chrome requires this option. It ensures every push message results in a visible notification. Setting it to false is not allowed.
- Sending pushes when subscription expires. Subscriptions expire or are revoked. Monitor 410 Gone responses and clean up expired subscriptions from your database.
- No notification click handling. Users tap notifications and nothing happens. Always handle notificationclick to open relevant content.
- Too many notifications. Sending multiple notifications in succession annoys users. Batch updates or use a summary notification.
- Ignoring notification permissions. Users may deny permissions. Handle the permission state gracefully and explain why notifications are useful before requesting.
Practice Questions
- What is the role of the push service in push notification architecture?
- Why must push subscriptions include userVisibleOnly: true?
- How do you handle notification click events in the service worker?
- What does a 410 error from the push service indicate?
- How do you generate VAPID keys for push notifications?
Challenge: Set up a complete push notification system: subscribe a user, store the subscription on a server, send a push message from the server, display the notification in the service worker, and handle the click event to open the app. Use the web-push Node.js library.
FAQ
Mini Project
Create a complete push notification system: a subscribe button on your PWA that requests permission and subscribes, a Node.js server endpoint that stores subscriptions, a send notification page that lets you compose and send a push message, and a service worker that displays notifications and handles clicks.
What's Next
You can send basic notifications. Now explore notification options — actions, badges, images, vibration patterns, and rich notification content.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro