Web Push Notifications
Service worker, VAPID keys, subscription management, and push events
What You’ll Build
After following this guide, you will have a working implementation of web push notifications in your project. Send push notifications to users even when they’re not on your site. Uses the Web Push API with VAPID keys, a service worker to receive pushes, and a server to trigger notifications. Works on desktop and Android (iOS Safari 16.4+).
Use Cases & Problems Solved
- Send transactional emails reliably without landing in spam folders
- Set up notification systems that scale with your user base
- Avoid building email delivery infrastructure from scratch
Prerequisites
- HTTPS-enabled site
- Node.js server for sending pushes
Step-by-Step Implementation
Generate VAPID keys and setup
The following snippet shows how to generate vapid keys and setup. Copy this into your project and adjust the values for your environment.
npx web-push generate-vapid-keys
# Save the public and private keys in your .env
Client-side subscription
The following snippet shows how to client-side subscription. Copy this into your project and adjust the values for your environment.
// Register service worker and subscribe to push
async function subscribeToPush() {
const registration = await navigator.serviceWorker.register('/sw.js');
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
});
// Send subscription to your server
await fetch('/api/push/subscribe', {
method: 'POST', body: JSON.stringify(subscription),
headers: { 'Content-Type': 'application/json' },
});
}
// sw.js — Service Worker
self.addEventListener('push', (event) => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body, icon: '/icon-192.png', badge: '/badge-72.png',
data: { url: data.url },
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Server-side push sending
The following snippet shows how to server-side push sending. Copy this into your project and adjust the values for your environment.
const webpush = require('web-push');
webpush.setVapidDetails('mailto:you@example.com', VAPID_PUBLIC, VAPID_PRIVATE);
async function sendPush(subscription, title, body, url) {
await webpush.sendNotification(subscription, JSON.stringify({ title, body, url }));
}
⚠️ Don’t Do This
❌ Requesting notification permission immediately on page load
// Users HATE this! Permission request with no context = always denied
window.addEventListener('load', () => {
Notification.requestPermission(); // Annoying popup on first visit!
});
✅ Ask for permission after user shows interest (e.g., clicks a bell icon)
// Show a custom UI explaining the value, then request permission
document.getElementById('notify-btn').addEventListener('click', async () => {
const permission = await Notification.requestPermission();
if (permission === 'granted') await subscribeToPush();
});
Testing
Verify your implementation with these tests:
// __tests__/web-push-notifications.test.ts
import { describe, it, expect } from 'vitest';
describe('Web Push Notifications', () => {
it('should initialize without errors', () => {
// Test that the setup completes successfully
expect(() => setup()).not.toThrow();
});
it('should handle the primary use case', async () => {
const result = await execute();
expect(result).toBeDefined();
expect(result.success).toBe(true);
});
it('should handle edge cases', async () => {
// Test with empty/null input
const result = await execute(null);
expect(result.error).toBeDefined();
});
});
Verification
# Subscribe to push notifications on your site
# Close the browser tab
# Send a push from your server:
curl -X POST http://localhost:3000/api/push/send -d '{"title":"Hello!"}'
# Should see native OS notification appear Related Specs
Email Templates with React Email
Responsive templates, components, preview server, and testing
Send Emails with Resend
React Email templates, attachments, batch sending, and delivery tracking