Real-Time Updates in Chrome Extensions: Web Push, GCM, and WebSockets (2026)

AppBooster Team · · 4 min read
Real-time data dashboard with live updates

Real-Time Updates for Chrome Extensions

Modern extensions need real-time capabilities — live notifications, server-pushed state updates, or bidirectional data streams. Chrome offers three approaches, each with different tradeoffs around battery life, complexity, and capability.


Comparing the Three Approaches

FeatureWeb Push (Push API)chrome.gcmWebSockets
DirectionServer → ClientServer → ClientBidirectional
Wakes suspended extensionYesYesNo
Battery impactLowLowHigher
Chrome version121+All versions116+
Standards-basedYesNo (legacy)Yes
Best forNotifications, state updatesLegacy broad messagingLive data, chat

The modern, standards-based approach. Web Push wakes your extension’s service worker when a message arrives — no keepalive needed.

Setup

{
  "manifest_version": 3,
  "permissions": ["notifications"],
  "background": {
    "service_worker": "service-worker.js"
  }
}

Subscribe to Push

// service-worker.js
async function subscribeToPush() {
  const registration = await self.registration;

  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(
      'YOUR_VAPID_PUBLIC_KEY'
    )
  });

  // Send subscription to your server
  await fetch('https://your-server.com/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription)
  });

  return subscription;
}

function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - base64String.length % 4) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const rawData = atob(base64);
  return new Uint8Array([...rawData].map(c => c.charCodeAt(0)));
}

Handle Push Events

self.addEventListener('push', (event) => {
  const data = event.data?.json() ?? {};

  event.waitUntil(
    self.registration.showNotification(data.title || 'Update', {
      body: data.body || 'You have a new update.',
      icon: 'icons/icon-128.png',
      badge: 'icons/badge-72.png',
      data: { url: data.url },
      actions: [
        { action: 'open', title: 'View' },
        { action: 'dismiss', title: 'Dismiss' }
      ]
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.action === 'open' || !event.action) {
    event.waitUntil(
      clients.openWindow(event.notification.data?.url || 'https://extensionbooster.com')
    );
  }
});

Server-Side (Node.js)

const webpush = require('web-push');

webpush.setVapidDetails(
  'mailto:your@email.com',
  'YOUR_VAPID_PUBLIC_KEY',
  'YOUR_VAPID_PRIVATE_KEY'
);

async function sendPush(subscription, payload) {
  await webpush.sendNotification(
    subscription,
    JSON.stringify(payload)
  );
}

// Send to a subscriber
sendPush(subscription, {
  title: 'Price Drop Alert',
  body: 'MacBook Pro is now $200 off!',
  url: 'https://example.com/deal'
});

Option 2: chrome.gcm (Legacy)

Built on deprecated Firebase Cloud Messaging HTTP protocol. Still works indefinitely for extensions but lacks modern features.

// Register for GCM
chrome.gcm.register(['YOUR_SENDER_ID'], (registrationId) => {
  if (chrome.runtime.lastError) {
    console.error(chrome.runtime.lastError.message);
    return;
  }
  // Send registrationId to your server
  sendToServer(registrationId);
});

// Listen for messages
chrome.gcm.onMessage.addListener((message) => {
  chrome.notifications.create({
    type: 'basic',
    iconUrl: 'icons/icon-128.png',
    title: message.data.title,
    message: message.data.body
  });
});

When to use: Only if you need to support Chrome versions before 121, or have existing GCM infrastructure.


Option 3: WebSockets

Best for frequent, bidirectional communication (chat, collaboration, live data). Requires keepalive to prevent service worker termination.

let ws = null;

function connect() {
  ws = new WebSocket('wss://your-server.com/ws');

  ws.onopen = () => {
    startKeepAlive();
    ws.send(JSON.stringify({ type: 'auth', token: 'user-token' }));
  };

  ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    handleRealtimeUpdate(data);
  };

  ws.onclose = () => {
    ws = null;
    stopKeepAlive();
    setTimeout(connect, 5000); // Reconnect
  };
}

function startKeepAlive() {
  setInterval(() => {
    if (ws?.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: 'ping' }));
    }
  }, 20000);
}

Key limitation: WebSockets cannot wake a suspended extension. Chrome has no awareness of connections initiated outside the extension, so if the service worker terminates, the connection is lost until something else wakes the worker.


Decision Framework

Use Web Push when:

  • You need to wake the extension from outside
  • Updates are infrequent (minutes to hours apart)
  • Battery life matters (mobile, laptops)
  • You want the simplest server-side implementation

Use WebSockets when:

  • You need bidirectional communication
  • Updates are frequent (seconds apart)
  • The extension is actively being used
  • You need low latency

Use chrome.gcm when:

  • You must support Chrome < 121
  • You have existing GCM/FCM infrastructure

What’s Next

Real-time capabilities transform your extension from a passive tool into an active assistant. Start with Web Push for notifications, add WebSockets when you need bidirectional communication, and always consider battery impact.

Discover more extension development strategies at ExtensionBooster.

Share this article

Build better extensions with free tools

Icon generator, MV3 converter, review exporter, and more — no signup needed.

Related Articles