Send FCM Event to All Opened Client Windows Instead of Focused (FCM Default)

Published on Mar 5, 2025 | Reading time: 2 min


Enhancing Multi-Tab Synchronization with Custom FCM Handling in Service Workers

Firebase Cloud Messaging (FCM) delivers push notifications by default to a browser’s focused tab. This article presents a service worker implementation that broadcasts FCM events to all active client windows, enabling real-time synchronization in multi-tab applications.

Core Implementation Strategy

The service worker employs the following key functionalities:

  1. Immediate Service Worker Activation

    • Uses skipWaiting() during installation and clients.claim() during activation to control pages instantly.
  2. Broadcasted Push Event Handling

    • Intercepts FCM push events, extracts payload data, and distributes messages to all open tabs via clients.matchAll(), ensuring universal event propagation.
  3. Notification Click Management

    • Captures notification clicks, encodes payload data into URL parameters, and directs users to a relevant page.
  4. Firebase Initialization

    • Configures Firebase with placeholder values to prevent initialization errors while maintaining custom push handling.

Optimized Service Worker Code

self.addEventListener('install', () => self.skipWaiting());  
self.addEventListener('activate', (event) => event.waitUntil(self.clients.claim()));  

self.importScripts('/firebase/firebase-app-compat.js', '/firebase/firebase-messaging-compat.js');  

self.addEventListener('notificationclick', (event) => {  
  event.notification.close();  
  const data = event.notification.data.FCM_MSG?.data;  
  if (data) {  
    const url = `/?pnBgClick=${btoa(JSON.stringify(data))}`;  
    event.waitUntil(self.clients.openWindow(url));  
  }  
});  

self.addEventListener('push', async (event) => {  
  const payload = event.data.json().data;  
  if (!payload) return;  
  const clients = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });  
  clients.forEach(client => client.postMessage({ type: 'push-notification', payload }));  
});  

firebase.initializeApp({ apiKey: true, projectId: true, messagingSenderId: true, appId: true });  
firebase.messaging();  

Client-Side Integration Example (React):

useEffect(() => {  
  const handler = (event) => { /* Handle message */ };  
  navigator.serviceWorker.addEventListener('message', handler);  
  return () => navigator.serviceWorker.removeEventListener('message', handler);  
}, []);  

Key Advantages

Conclusion

This implementation addresses FCM’s default single-tab targeting by leveraging service workers to broadcast events universally. Ideal for collaborative tools, chat applications, or any multi-tab interface requiring real-time synchronization, this approach guarantees consistent user experiences across all active sessions.