Skip to content
Developers · 10 min read

The Web Push API in JavaScript: a working example

Browser push takes four pieces of code: register a service worker, ask permission after a click, subscribe with your VAPID public key, and send the subscription to your server. Here they are, end to end.

Updated By the Pusharoo team
In short
  • navigator.serviceWorker.register() installs the worker that will receive pushes.
  • Notification.requestPermission() must run in response to a click in Firefox and Safari.
  • registration.pushManager.subscribe() needs userVisibleOnly: true and your VAPID public key.
  • Send the resulting PushSubscription JSON to your server; it’s the address you push to.

The moving parts

PieceRuns inJob
Page scriptYour web pageRegisters the worker, asks permission, subscribes
Service worker (/sw.js)Background, browser-managedReceives push events, shows notifications
Your serverBackendStores subscriptions, signs and encrypts messages
Push serviceBrowser vendorDelivers to the device

Conceptual overview: how web push works.

1. Register the service worker

if ('serviceWorker' in navigator && 'PushManager' in window) { const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/', updateViaCache: 'none' // always check for a fresh sw.js }); }

Serve sw.js from your root so its scope covers the whole site. A worker at /js/sw.js only controls /js/ unless you send a Service-Worker-Allowed header.

2. Ask permission after a click

document.querySelector('#notify-btn').addEventListener('click', async () => { const permission = await Notification.requestPermission(); if (permission !== 'granted') return; // 'denied' or 'default' await subscribe(); });

Firefox and Safari ignore permission requests that don’t follow a user gesture, and Chrome may quiet sites that ask on load. Show your own explanation first (two-step opt-in).

3. Subscribe with your VAPID public key

function urlBase64ToUint8Array(base64) { const pad = '='.repeat((4 - base64.length % 4) % 4); const raw = atob((base64 + pad).replace(/-/g, '+').replace(/_/g, '/')); return Uint8Array.from([...raw].map(c => c.charCodeAt(0))); } async function subscribe() { const registration = await navigator.serviceWorker.ready; let sub = await registration.pushManager.getSubscription(); if (!sub) { sub = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) }); } await fetch('/api/push/subscriptions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(sub) // { endpoint, expirationTime, keys: { p256dh, auth } } }); }

The public key is safe to ship in your page; the private key must stay on the server (VAPID keys).

4. Show the notification in the worker

// sw.js self.addEventListener('push', event => { const data = event.data ? event.data.json() : {}; event.waitUntil(self.registration.showNotification(data.title || 'Update', { body: data.body, icon: data.icon || '/icon-192.png', data: { url: data.url || '/' } })); }); self.addEventListener('notificationclick', event => { event.notification.close(); event.waitUntil(clients.openWindow(event.notification.data.url)); });

More patterns — focusing an open tab, action buttons, subscription changes — in service worker push events.

5. Send from your server

Sending means encrypting the payload for that subscription’s keys, signing a VAPID JWT and POSTing to the endpoint. Don’t hand-roll the crypto; use a maintained library: web-push for Node.js, minishlink/web-push for PHP, or pywebpush for Python. Protocol details: the Web Push protocol.

Or skip the plumbing

If you’d rather not run subscription storage, a sending queue, 404/410 cleanup, a soft prompt and an iPhone Home Screen flow yourself, Pusharoo does all of it for free: upload sw.js, add one script tag, and use Pusharoo.subscribe() from your own button (JavaScript API).

Questions

Do I need Firebase to use the Push API?

No. The Push API works with standard VAPID keys in every major browser. Firebase is optional (comparison).

Why does pushManager.subscribe() fail?

Common causes: permission not granted, page not on HTTPS, an invalid applicationServerKey, or (on iPhone) the page isn’t running as a Home Screen web app.

Can I send a push from JavaScript in the browser?

No — sending requires your VAPID private key, which must never be exposed in the browser. Send from a server.

More in Developers: Web push notifications in PHP · Laravel browser push notifications (no Firebase) · Web push notifications in React and Next.js · Declarative Web Push, explained · Firebase web push vs. standard web push · All guides

Free for life. No limits.

Unlimited subscribers, campaigns and automations. No badges. About five minutes to install.