Notifications, Caching, and Messages in a Progressive Web App (PWA)

Master the implementation of push notifications, offline caching, and worker-client communication to build compelling cross-platform mobile experiences

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

StrategyDescriptionBest For
Cache FirstServe from cache, fallback to networkStatic assets
Network FirstTry network, fallback to cacheFrequently updated content
Stale While RevalidateServe cache immediately, update in backgroundBalance 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');
 }
 })
 );
});
Core PWA Notification and Caching Capabilities

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.

Build Powerful Cross-Platform Mobile Experiences

Our team specializes in Progressive Web Apps and cross-platform mobile development. Let us help you implement notifications, caching, and advanced mobile features that engage users and drive results.

Sources

  1. MDN Web Docs - Make PWAs re-engageable using Notifications and Push APIs - Comprehensive tutorial on Push and Notifications APIs
  2. LogRocket - Notifications, caching, and messages in a PWA - Detailed implementation guide covering local notifications and message passing
  3. MobiLoud - How to Set Up Push Notifications for Your PWA - Cross-platform guide covering iOS and Android PWA notification support
  4. MDN Web Docs - Push API - Official documentation for Push API implementation
  5. MDN Web Docs - Notifications API - Official documentation for browser notifications
  6. MDN Web Docs - Service Worker API - Service worker implementation reference