Overview
Web push notifications allow you to re-engage users even after they've left your website — no app required. They work through a combination of the Push API, Notifications API, and Service Workers. This guide walks through the complete implementation from scratch.
Prerequisites
- A website served over HTTPS (required by all browsers)
- Basic knowledge of JavaScript
- A VAPID key pair (Voluntary Application Server Identification)
- A server-side component to store subscriptions and send push messages
Step 1: Register a Service Worker
A service worker is a background script that intercepts push events even when your page isn't open. Register it in your main JavaScript file:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('Service Worker registered', reg))
.catch(err => console.error('Registration failed', err));
}
Step 2: Request Notification Permission
Before subscribing, you must request user permission. Do this in response to a user gesture (like a button click) — browsers block programmatic permission prompts:
async function requestPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
await subscribeUser();
}
}
Step 3: Create a Push Subscription
Using your VAPID public key, subscribe the user through the service worker registration:
async function subscribeUser() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(YOUR_PUBLIC_VAPID_KEY)
});
// Send subscription object to your server
await saveSubscription(subscription);
}
The urlBase64ToUint8Array helper converts your base64 VAPID key to the format the API expects. Numerous open-source implementations are available on GitHub.
Step 4: Handle Push Events in the Service Worker
Inside your sw.js file, listen for the push event and display a notification:
self.addEventListener('push', event => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge-72.png',
data: { url: data.url }
})
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Step 5: Send a Push Message from Your Server
On the server side, use the web-push library (Node.js) to send notifications to stored subscriptions:
const webpush = require('web-push');
webpush.setVapidDetails(
'mailto:you@example.com',
PUBLIC_VAPID_KEY,
PRIVATE_VAPID_KEY
);
const payload = JSON.stringify({
title: 'New Article Published',
body: 'Check out our latest guide on push notifications.',
url: 'https://example.com/articles/new-guide'
});
webpush.sendNotification(subscription, payload);
Browser Compatibility Notes
- Chrome, Edge, Firefox: Full support
- Safari (macOS 13+, iOS 16.4+): Supported via Web Push standard
- Older iOS: Not supported — consider in-app fallbacks
Next Steps
Once basic notifications are working, explore advanced features like notification actions, images, silent pushes for background data sync, and analytics tracking via notification click events.