0Pricing
Frontend Academy · Lesson

Push Notifications

Request permission, subscribe to push with the Push API, receive messages in the Service Worker, and display notifications with showNotification.

Push Notifications is a free Frontend Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Push Notifications?

Push notifications re-engage users when the app isn't open — message alerts, news updates, abandoned cart reminders. Powered by the Service Worker (so they work even when the page is closed).

The Push API + Notifications API

Two browser APIs work together. The Push API receives messages from a server via a push subscription. The Notifications API displays the message to the user.

Requesting Permission

Ask the user for permission before subscribing — only do this in response to a clear user action (button click), not on page load.

async function enableNotifications() {
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') {
    alert('Notifications denied');
    return;
  }
  // proceed to subscribe...
}

Push Subscription

Subscribe through the active service worker registration. The server's VAPID public key authenticates the subscription.

async function subscribeToPush() {
  const reg = await navigator.serviceWorker.ready;
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true, // required: every push must show a notification
    applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
  });
  // Send sub to your server:
  await fetch('/api/save-subscription', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(sub)
  });
}

VAPID Keys

VAPID (Voluntary Application Server Identification) keys identify your server to push services (FCM, Mozilla, etc.). Generate once and store the private key on your server.

# Generate VAPID keys with web-push CLI:
npx web-push generate-vapid-keys

# Output:
# Public Key: BJ...
# Private Key: ka...

Server-Side Push

Use the web-push npm library to send a push to a stored subscription.

import webpush from 'web-push';

webpush.setVapidDetails(
  'mailto:you@example.com',
  process.env.VAPID_PUBLIC,
  process.env.VAPID_PRIVATE
);

await webpush.sendNotification(savedSubscription, JSON.stringify({
  title: 'New message',
  body: 'Alice sent you a message',
  url: '/inbox/123'
}));

Handling Push in the Service Worker

The SW receives the push event and shows a notification.

// sw.js
self.addEventListener('push', (event) => {
  const data = event.data ? event.data.json() : {};
  event.waitUntil(
    self.registration.showNotification(data.title || 'Notification', {
      body: data.body,
      icon: '/icons/192.png',
      badge: '/icons/badge.png',
      data: { url: data.url }
    })
  );
});

Notification Click Handler

Open or focus the app when the user clicks a notification.

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  const url = event.notification.data.url || '/';
  event.waitUntil(
    clients.matchAll({ type: 'window' }).then(clientList => {
      for (const client of clientList) {
        if (client.url.endsWith(url) && 'focus' in client) return client.focus();
      }
      return clients.openWindow(url);
    })
  );
});

Notification Options

Customise: icon, badge (small notification area icon), image (large), actions (buttons), tag (replace older notifications with same tag), requireInteraction (stay until dismissed), silent.

self.registration.showNotification('Message', {
  body: 'Alice sent you a photo',
  icon: '/icons/192.png',
  image: '/images/alice-photo.jpg',
  actions: [
    { action: 'view', title: 'View' },
    { action: 'reply', title: 'Reply' }
  ],
  tag: 'message-from-alice', // replaces earlier 'message-from-alice'
  requireInteraction: false
});

Unsubscribing

Let users opt out — call pushSubscription.unsubscribe() and remove the subscription from your server.

async function disablePush() {
  const reg = await navigator.serviceWorker.ready;
  const sub = await reg.pushManager.getSubscription();
  if (sub) {
    await sub.unsubscribe();
    await fetch('/api/delete-subscription', {
      method: 'POST',
      body: JSON.stringify({ endpoint: sub.endpoint })
    });
  }
}

iOS Support

iOS 16.4+ supports Web Push, but only for PWAs installed to the Home Screen. Pre-iOS-16.4 devices and Safari on the iPhone web don't support web push at all.

Best Practices

1) Ask permission only after a clear user action. 2) Send meaningful, infrequent notifications. 3) Always show a notification on a push event (browsers may revoke if you don't). 4) Provide an easy unsubscribe. 5) Test with all 3 push services (FCM, Mozilla, WindowsPush).

Common Pitfalls

Push fails silently if: userVisibleOnly is false (Chrome requires true), VAPID keys mismatched, subscription expired (re-subscribe), no notification shown on push event (browser revokes).

Quick Check

Why must every push message result in a visible notification (with userVisibleOnly: true)?

Recap: Push Notifications

Request permission via user action only. Subscribe via reg.pushManager.subscribe with VAPID public key. Server uses web-push library to send. SW push event calls showNotification. notificationclick opens or focuses your app. Customise icon/badge/image/actions/tag. Always show a notification (userVisibleOnly: true required). iOS 16.4+ supports it, PWA-installed only.

Frequently asked questions

Is the “Push Notifications” lesson free?

Yes — the full text of “Push Notifications” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “Push Notifications”?

Request permission, subscribe to push with the Push API, receive messages in the Service Worker, and display notifications with showNotification. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Push Notifications” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Web App Manifest
  2. Service Worker Registration and Lifecycle
  3. Offline Caching Strategies: CacheFirst NetworkFirst
  4. Push Notifications
← Back to Frontend Academy