Guide to Implementing Web Push Notifications
Learn how to set up and send web push notifications to re-engage users. This guide covers client-side setup and server-side logic.
Modern web applications constantly seek ways to keep users engaged and informed. A common challenge developers face is how to communicate with users effectively after they leave the website. Without a direct channel, users might forget to return, leading to decreased engagement and retention. This guide walks you through implementing web push notifications, a powerful tool for direct user communication.
Understanding Web Push Notifications
Web push notifications are messages that your website can send to users. These messages appear on their desktop or mobile device, similar to native application notifications. The unique aspect is that they function even when the user is not actively browsing your website. This capability relies on two key web technologies: Service Workers and the Push API.
Service Workers are JavaScript files that run in the background, separate from the main browser thread. They can intercept network requests, cache resources, and receive push messages from a server. The Push API, on the other hand, allows a web application to subscribe a user to push services and receive messages from a backend server.
Setting Up the Client-Side
The client-side setup involves registering a service worker and requesting user permission.
-
Register Your Service Worker: First, create a JavaScript file, for example,
service-worker.js, in your project's root directory. Then, register it from your main application script.// In your main application JavaScript file (e.g., app.js) if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('Service Worker registered with scope:', registration.scope); }) .catch(error => { console.error('Service Worker registration failed:', error); }); }); }Inside
service-worker.js, you will handle push events:// service-worker.js self.addEventListener('push', event => { const data = event.data.json(); console.log('Push received:', data); const title = data.title || '2HAAS Notification'; const options = { body: data.body || 'You have a new message!', icon: '/images/icon-192x192.png', // Ensure you have an icon badge: '/images/badge-72x72.png' // For Android, optional }; event.waitUntil(self.registration.showNotification(title, options)); }); self.addEventListener('notificationclick', event => { console.log('Notification clicked!'); event.notification.close(); // You can open a new window or focus an existing one here event.waitUntil( clients.openWindow('https://2haas.com') ); }); -
Request User Permission and Subscribe: After the service worker is registered, you need to ask the user for permission to send notifications. If permission is granted, you can subscribe the user to a push service. This process generates a
PushSubscriptionobject.// In your main application JavaScript file (e.g., app.js) async function subscribeUser() { if (!('PushManager' in window)) { console.warn('Push notifications are not supported by this browser.'); return; } const permissionResult = await Notification.requestPermission(); if (permissionResult !== 'granted') { console.warn('Permission for notifications was not granted.'); return; } const serviceWorkerRegistration = await navigator.serviceWorker.ready; const applicationServerKey = urlBase64ToUint8Array('YOUR_VAPID_PUBLIC_KEY'); // Replace with your key const subscription = await serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKey }); console.log('User is subscribed:', subscription); // Send the subscription object to your backend server await fetch('/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); console.log('Subscription sent to server.'); } // Utility function to 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; } // Call subscribeUser() when appropriate, e.g., on a button click // document.getElementById('subscribe-button').addEventListener('click', subscribeUser);
Server-Side Implementation
On the server, you need to store user subscriptions and send push notifications using a push library.
-
Generate VAPID Keys: VAPID (Voluntary Application Server Identification) keys authenticate your server to the push service. You can generate a pair of public and private keys using a library or an online tool. Keep the private key secure and use the public key in your client-side code.
# Example using web-push-cli in Node.js environment npx web-push generate-vapid-keys # This will output: # Public Key: B... # Private Key: ... -
Store Subscriptions: When a user subscribes, your client-side code sends the
PushSubscriptionobject to your backend. Store this object, typically in a database, associated with the user. Each subscription contains anendpointURL andkeys(p256dh and auth).// Example PushSubscription object { "endpoint": "https://fcm.googleapis.com/fcm/send/...", "expirationTime": null, "keys": { "p256dh": "...", "auth": "..." } } -
Send Push Notifications: To send a notification, your server uses a web push library (e.g.,
web-pushfor Node.js,pywebpushfor Python). This library encrypts the payload and sends it to the push service endpoint specified in the user's subscription.// Example Node.js server-side code using the 'web-push' library const webpush = require('web-push'); // Replace with your generated VAPID keys and contact email webpush.setVapidDetails( 'mailto:[email protected]', 'YOUR_VAPID_PUBLIC_KEY', 'YOUR_VAPID_PRIVATE_KEY' ); // Assuming you have an array of stored subscriptions // const subscriptions = getSubscriptionsFromDatabase(); app.post('/send-notification', async (req, res) => { const payload = JSON.stringify({ title: 'New Article from 2HAAS!', body: 'Check out our latest guide on Web Push Notifications.', url: 'https://2haas.com/blog/implement-web-push-notifications' // Optional URL for notification click }); // Iterate through stored subscriptions and send for (const subscription of subscriptions) { try { await webpush.sendNotification(subscription, payload); console.log('Notification sent to:', subscription.endpoint); } catch (error) { console.error('Failed to send notification:', error); // Handle expired or invalid subscriptions (e.g., remove from database) } } res.status(200).send('Notifications sent successfully.'); });
Best Practices and Considerations
- User Experience: Always ask for permission at an opportune moment, not immediately on page load. Explain the value proposition.
- Payload Size: Keep notification payloads small. Many push services have size limits (e.g., 4KB).
- Expiration: Push services might hold notifications for a limited time if the device is offline. You can specify a
ttl(Time To Live) in your push request. - Unsubscribing: Provide a clear way for users to unsubscribe. Remove their subscription from your database when they do.
- Error Handling: Implement robust error handling on the server, especially for
410 Gonestatus codes, which indicate an expired or invalid subscription that should be removed.
Your Notification Checklist
Implementing web push notifications can significantly boost user engagement. Follow these steps:
- Ensure your application is served over HTTPS.
- Create and register your
service-worker.js. - Generate VAPID keys for server authentication.
- Implement client-side permission requests and subscription logic.
- Develop server-side code to store subscriptions and send notifications.
- Test thoroughly across different browsers and devices.
- Provide a clear unsubscribe mechanism for users.