TrueSpec

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

Beginner

Email Templates with React Email

Responsive templates, components, preview server, and testing

Email & Notifications
Beginner

Send Emails with Resend

React Email templates, attachments, batch sending, and delivery tracking

Email & Notifications
Intermediate

In-App Notification Center

Bell icon, unread count, real-time updates, and mark-as-read

Email & Notifications