Web APIs Cheatsheet
Notifications
Use this Web APIs reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Permission
// Check current state Notification.permission; // 'default' | 'granted' | 'denied' // Request permission (must be called in response to user gesture) const result = await Notification.requestPermission(); // result: 'granted' | 'denied' | 'default'
Browsers may silently ignore
requestPermission()if not triggered by a user gesture (click, keypress, etc.).
Showing a Notification (Page Context)
if (Notification.permission === 'granted') { const n = new Notification('Title', { body: 'Notification body text.', icon: '/icons/notification-icon.png', // image URL badge: '/icons/badge.png', // small monochrome icon (Android) image: '/img/preview.jpg', // large preview image tag: 'thread-1', // group/replace same-tag notifications data: { url: '/messages/42' }, // arbitrary data, no display silent: false, // mute sound/vibration requireInteraction: false, // keep notification visible until user acts renotify: false, // re-notify if same tag replaced dir: 'auto', // text direction: 'auto' | 'ltr' | 'rtl' lang: 'en', // language tag vibrate: [200, 100, 200], // vibration pattern ms (mobile) timestamp: Date.now(), // when the event occurred }); }
Notification Events (page context)
n.addEventListener('click', e => { window.focus(); n.close(); }); n.addEventListener('close', () => {}); n.addEventListener('error', e => console.error(e)); n.addEventListener('show', () => {}); // Programmatically close n.close();
Service Worker Notifications (Recommended)
Service Worker notifications work when the page is closed. Always prefer this approach.
// page.js — ask SW to show notification const reg = await navigator.serviceWorker.ready; await reg.showNotification('New message', { body: 'Alice: Hey, are you free?', icon: '/icons/icon-192.png', badge: '/icons/badge.png', image: '/img/chat-preview.jpg', tag: 'msg-thread-1', renotify: true, requireInteraction: true, data: { url: '/messages/42', threadId: 1 }, actions: [ { action: 'reply', title: 'Reply', icon: '/icons/reply.png' }, { action: 'dismiss', title: 'Dismiss', icon: '/icons/close.png' }, ], vibrate: [100, 50, 100], silent: false, timestamp: Date.now(), });
Notification Options Full Reference
| Option | Type | Description |
|---|---|---|
body | string | Secondary text |
icon | string | URL of notification icon |
badge | string | Small monochrome icon (Android status bar) |
image | string | Large image shown in notification |
tag | string | Notification identifier; same tag replaces previous |
renotify | boolean | Re-alert even if same tag already shown |
requireInteraction | boolean | Do not auto-dismiss |
silent | boolean | No sound or vibration |
vibrate | number[] | Vibration pattern in ms (on/off alternating) |
timestamp | number | Event time (ms since epoch) |
dir | string | 'auto' 'ltr' 'rtl' |
lang | string | BCP 47 language tag |
data | any | Arbitrary data (structured clone) |
actions | NotificationAction[] | Up to 2 action buttons (SW only) |
NotificationAction
{
action: 'reply', // action identifier
title: 'Reply', // button label
icon: '/icons/reply.png', // button icon
}Service Worker Event Handlers
// sw.js // User clicked notification body self.addEventListener('notificationclick', e => { e.notification.close(); const { url } = e.notification.data; if (e.action === 'reply') { e.waitUntil(clients.openWindow(`${url}?reply=true`)); return; } if (e.action === 'dismiss') { return; // already closed } // Default click — focus existing window or open new one e.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then(list => { for (const client of list) { if (client.url === url) return client.focus(); } return clients.openWindow(url); }) ); }); // Notification dismissed by user or OS self.addEventListener('notificationclose', e => { const { data } = e.notification; // Analytics: log that user dismissed });
Web Push (background notifications)
// 1. Subscribe const reg = await navigator.serviceWorker.ready; const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY), }); // sub.endpoint, sub.keys.p256dh, sub.keys.auth // Send sub to your server to store // 2. Check existing subscription const existing = await reg.pushManager.getSubscription(); // 3. Unsubscribe await sub.unsubscribe(); // Helper to convert VAPID key function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const raw = atob(base64); return Uint8Array.from([...raw].map(c => c.charCodeAt(0))); }
// sw.js — receive push self.addEventListener('push', e => { if (!e.data) return; const payload = e.data.json(); // or e.data.text(), e.data.arrayBuffer() e.waitUntil( self.registration.showNotification(payload.title, { body: payload.body, icon: payload.icon, data: payload.data, }) ); }); // Push subscription change (key rotation) self.addEventListener('pushsubscriptionchange', e => { e.waitUntil( e.newSubscription ? sendToServer(e.newSubscription) : reg.pushManager.subscribe(subscribeOptions).then(sendToServer) ); });
Listing and Closing Active Notifications
const reg = await navigator.serviceWorker.ready; // Get all notifications (optional filter by tag) const notifications = await reg.getNotifications({ tag: 'thread-1' }); notifications.forEach(n => n.close());
Notification.permission States
| State | Meaning |
|---|---|
'default' | Not yet asked; notifications will not show |
'granted' | User allowed; notifications will show |
'denied' | User blocked; requestPermission always returns 'denied' |
Once denied, there is no programmatic way to re-prompt. Users must change permission in browser settings.
Gotchas
- Page notifications (
new Notification()) don't work in Service Worker scope and don't persist across page close.requireInteractionis not supported on mobile browsers (notifications always auto-dismiss).actionsare only available in Service Worker notifications, and limited to 2 buttons on most platforms.- Push notifications require a VAPID key pair, a push subscription endpoint, and a server that can send to the Web Push protocol.
- Silent push (
e.datais null) can wake a Service Worker but may be throttled by browsers to prevent abuse.- The browser may suppress notifications if the user has not interacted with the site recently (quiet UI permission).
Quick Permission Pattern
async function askNotificationPermission() { if (!('Notification' in window)) return false; if (Notification.permission === 'granted') return true; if (Notification.permission === 'denied') return false; const result = await Notification.requestPermission(); return result === 'granted'; } button.addEventListener('click', async () => { if (await askNotificationPermission()) { new Notification('Notifications enabled!'); } });