Progressive Web Apps have transformed how we think about mobile experiences on the web. Two of the most powerful features that PWAs offer are the ability to send notifications and cache content for offline use. Together, these capabilities enable apps to re-engage users even when the browser is closed, while ensuring critical content remains accessible regardless of network conditions.
This guide explores how to implement notifications, caching, and messaging in your PWA to create truly compelling mobile experiences. For developers working with cross-platform mobile technologies, understanding these mechanisms is essential. While React Native and native iOS/Android developers might be familiar with push notifications, PWAs offer a unique approach that works across all platforms with a single codebase.
Understanding the Foundation: Service Workers in PWAs
Service workers are the technological foundation that makes notifications, push messaging, and offline caching possible in progressive web applications. They act as a proxy between the web application and the network, running in the background independently of the main browser thread. This background execution model is what enables PWAs to receive and process push messages even when the application tab is closed or the browser is not running.
What Service Workers Enable
At their core, service workers provide three critical capabilities for PWA development:
- Network interception: Service workers intercept network requests and can serve cached responses, enabling true offline functionality
- Push notifications: They can receive and handle push notifications from application servers
- Background sync: They enable operations that defer until network connectivity is restored
Service Worker Scope and Registration
Proper service worker registration and scope configuration are essential for effective implementation. The service worker must be registered with an appropriate scope that covers all the pages it needs to control.
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker registered:', registration.scope);
})
.catch(error => {
console.log('Service Worker registration failed:', error);
});
});
}
For comprehensive documentation on service worker implementation, refer to the Service Worker API reference on MDN Web Docs.
The Notifications API: Engaging Users with Local Notifications
The Notifications API provides a standardized way to display notifications to users outside the browser window. These notifications appear in the operating system's notification center, similar to those from native applications.
Requesting Notification Permission
Before displaying notifications, applications must obtain explicit permission from the user. This is a critical user experience consideration, as browsers require user action to grant notification permissions.
The permission can be in one of three states:
- granted: User has approved notifications
- denied: User has blocked notifications
- default: User hasn't made a choice yet
Creating and Displaying Notifications
Once permission is granted, applications can create and display notifications using the Notification constructor. Notifications can include various options such as title, body text, icon, badge, vibration patterns, and action buttons.
function showNotification(title, options) {
if (Notification.permission === 'granted') {
const notification = new Notification(title, {
body: options.body || '',
icon: options.icon || '/icons/notification-icon.png',
badge: options.badge || '/icons/badge-72x72.png',
tag: options.tag || 'default',
requireInteraction: options.requireInteraction || false
});
notification.onclick = (event) => {
event.preventDefault();
window.focus();
notification.close();
};
}
}
For detailed information on notification options and behavior, consult the Notifications API documentation on MDN Web Docs.
Push API: Server-Triggered Notifications
The Push API enables servers to send messages to PWAs even when the application is not actively running. This capability is essential for delivering timely information such as news alerts, messaging notifications, and real-time updates.
Push API Architecture Overview
The Push API architecture involves several components working together:
- Application frontend: Registers for push and receives subscriptions
- Service worker: Receives push messages in the background
- Push service: Delivers messages from servers to devices
- Application server: Sends push messages via the push service
VAPID Authentication
Voluntary Application Server Identification (VAPID) provides a standardized method for identifying the application server to push services. VAPID uses public-key cryptography to authenticate push requests.
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
});
// Send subscription to server
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
return subscription;
}
For complete implementation details, review the Push API documentation on MDN Web Docs.
Caching Strategies for Offline Message Storage
Effective caching is essential for PWAs that need to function reliably regardless of network conditions. Caching strategies determine how the service worker handles requests.
Common Caching Strategies
| Strategy | Description | Best For |
|---|---|---|
| Cache First | Serve from cache, fallback to network | Static assets |
| Network First | Try network, fallback to cache | Frequently updated content |
| Stale While Revalidate | Serve cache immediately, update in background | Balance of speed and freshness |
Implementing Cache Storage for Messages
For message caching, IndexedDB is often preferable because it supports complex queries and can store larger amounts of structured data.
async function storeMessage(message) {
const db = await openMessageDatabase();
const transaction = db.transaction(['messages'], 'readwrite');
const store = transaction.objectStore('messages');
const enrichedMessage = {
...message,
storedAt: Date.now(),
read: false,
synced: false
};
store.add(enrichedMessage);
return enrichedMessage;
}
Implementing robust offline storage is crucial for maintaining cross-platform mobile experiences. To learn more about offline data persistence strategies, explore our guide on offline storage for PWAs.
Worker-Client Communication
Service workers and application clients need to communicate to coordinate notification handling, share state, and respond to user interactions.
Using MessageChannel for Bidirectional Communication
MessageChannel creates a communication channel between two browsing contexts, such as between the main page and the service worker.
// In the main page: establish communication channel
const messageChannel = new MessageChannel();
messageChannel.port1.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'NEW_MESSAGE') {
displayNewMessage(data);
}
};
// Send port to service worker
navigator.serviceWorker.controller.postMessage(
{ type: 'CONNECT_PORT' },
[messageChannel.port2]
);
Effective client communication enables seamless notification experiences that keep users engaged across sessions. This approach is fundamental to building professional mobile applications that users can rely on.
Best Practices for PWA Notifications and Caching
Notification UX Guidelines
- Timely and relevant: Deliver information that matters to the user at the appropriate moment
- Avoid notification spam: Excessive notifications lead to user frustration and permission revocation
- Provide controls: Allow users to customize notification preferences
- Concise content: Include relevant metadata without overwhelming the user
Performance Optimization
- Complete service worker work quickly to avoid termination
- Use efficient data structures for message storage
- Implement cache eviction policies
- Monitor cache performance using the Storage API
Cross-Platform Considerations
PWA notification and caching behavior varies across platforms. iOS PWAs have more limited push notification support compared to Android. Test notification functionality thoroughly across target platforms. For deeper understanding of Android integration patterns, consider our guide on Android intent filters.
For additional context on cross-platform PWA capabilities, refer to the comprehensive tutorial on making PWAs re-engageable using Notifications and Push APIs.
Complete Implementation Example
// Service Worker - push event handling
self.addEventListener('push', (event) => {
let data = { title: 'New Notification', body: 'You have a new message' };
if (event.data) {
try {
data = event.data.json();
} catch (e) {
data.body = event.data.text();
}
}
const options = {
body: data.body,
icon: '/icons/notification-icon.png',
badge: '/icons/badge-72x72.png',
vibrate: [100, 50, 100],
data: data.data || {},
actions: [
{ action: 'open', title: 'Open' },
{ action: 'dismiss', title: 'Dismiss' }
]
};
event.waitUntil(
(async () => {
await storeMessage({ ...data, receivedAt: Date.now() });
self.registration.showNotification(data.title, options);
})()
);
});
// Handle notification clicks
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.action === 'dismiss') return;
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then((clientList) => {
for (const client of clientList) {
if (client.url.includes('/messages') && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow('/messages');
}
})
);
});
Essential features for building robust progressive web applications
Service Workers
Background scripts that enable offline functionality, push notifications, and background sync operations
Notifications API
Display system-level notifications that engage users even when the browser is closed
Push API
Server-initiated messaging that delivers real-time updates to users across platforms
Offline Caching
Store content locally for reliable access regardless of network conditions
Message Passing
Coordinate between service workers and application clients for seamless experiences
Background Sync
Defer operations until network connectivity is restored for reliable data synchronization
Frequently Asked Questions
How do PWA push notifications differ from native app notifications?
PWA push notifications use the same system-level notification infrastructure as native apps, appearing in the notification center alongside native app notifications. The key difference is that PWAs are built with web technologies and work across platforms with a single codebase.
Can PWAs send push notifications on iOS?
iOS PWAs have more limited push notification support compared to Android. While local notifications are well-supported, push notifications require special handling on iOS. Recent iOS updates have improved PWA capabilities, but platform differences should be considered in your implementation.
What is VAPID and why is it required for push notifications?
VAPID (Voluntary Application Server Identification) is a security mechanism that identifies your application server to push services. It uses public-key cryptography to ensure only authorized servers can send push messages to your users, preventing unauthorized notification spam.
How much data can PWAs cache offline?
Modern browsers typically provide generous storage quotas for PWAs, though limits vary by browser and device. The Storage API allows you to check available storage, and implementing cache eviction policies helps manage storage effectively for long-running applications.
What happens if a user receives a push notification while offline?
Push notifications require an active connection to the push service to be delivered. If the device is offline when a push message is sent, the push service will queue the message and attempt delivery when connectivity is restored, typically for a limited time period.
Sources
- MDN Web Docs - Make PWAs re-engageable using Notifications and Push APIs - Comprehensive tutorial on Push and Notifications APIs
- LogRocket - Notifications, caching, and messages in a PWA - Detailed implementation guide covering local notifications and message passing
- MobiLoud - How to Set Up Push Notifications for Your PWA - Cross-platform guide covering iOS and Android PWA notification support
- MDN Web Docs - Push API - Official documentation for Push API implementation
- MDN Web Docs - Notifications API - Official documentation for browser notifications
- MDN Web Docs - Service Worker API - Service worker implementation reference