🚀AdvancedReal-Time Communication

Broadcast Channel API

Communicate in real time between multiple browser tabs, windows, or workers from the same origin using the BroadcastChannel API.

Overview

The Broadcast Channel API enables simple messaging between all browsing contexts (tabs, windows, iframes, service workers, shared workers) that share the same origin. All contexts subscribed to a named channel receive messages broadcast on it. This is ideal for cross-tab synchronization: syncing authentication state (logout in one tab, logout everywhere), keeping UI in sync across multiple open tabs, notifying tabs when background service workers update the cache, or coordinating work across Web Workers. The API is intentionally simple: `new BroadcastChannel("channel-name")` creates or joins a channel. `channel.postMessage(data)` broadcasts to all other contexts. `channel.onmessage` receives broadcasts. `channel.close()` disconnects.

Key Points

  • •`new BroadcastChannel("name")` joins a named channel all same-origin contexts on the same channel receive messages
  • •`channel.postMessage(data)` sends to ALL other listeners on the channel (sender does NOT receive its own message)
  • •`channel.onmessage = handler` or `channel.addEventListener("message", handler)` to receive
  • •Messages are structured-cloned can send objects, arrays, typed arrays, not functions
  • •Sender does NOT receive its own broadcast
  • •Works between: tabs, iframes (same origin), service workers, shared workers, dedicated workers
  • •`channel.close()` frees resources and removes from the channel important in SPAs to prevent leaks

Examples

Cross-tab authentication synchronization

<!-- In every tab/page of your app -->
<script>
const authChannel = new BroadcastChannel('auth');

// Broadcast logout to all tabs
function logout() {
  clearLocalStorage();
  authChannel.postMessage({ type: 'LOGOUT' });
  window.location.href = '/login';
}

// Receive logout from another tab
authChannel.onmessage = (event) => {
  if (event.data.type === 'LOGOUT') {
    clearLocalStorage();
    window.location.href = '/login';
  }
  if (event.data.type === 'LOGIN') {
    // Another tab logged in refresh the page to pick up the session
    window.location.reload();
  }
};

// Broadcast login event
function onLoginSuccess(user) {
  authChannel.postMessage({ type: 'LOGIN', user });
}

// Clean up when the tab closes
window.addEventListener('beforeunload', () => authChannel.close());
</script>

A single logout in any tab propagates to all open tabs essential for security-sensitive apps.

Service worker notifying all tabs of a cache update

// In sw.js
const updateChannel = new BroadcastChannel('sw-updates');

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v2').then(cache =>
      cache.addAll(['/app.js', '/styles.css', '/'])
    ).then(() => {
      // Notify all tabs that new content is available
      updateChannel.postMessage({
        type: 'UPDATE_AVAILABLE',
        version: 'v2'
      });
    })
  );
});

// In the page
const swChannel = new BroadcastChannel('sw-updates');
swChannel.onmessage = (event) => {
  if (event.data.type === 'UPDATE_AVAILABLE') {
    // Show banner: "New version available click to refresh"
    document.getElementById('update-banner').style.display = 'block';
  }
};

The service worker broadcasts to tabs a clean decoupled alternative to service worker postMessage.

Shared counter across multiple tabs

<div>
  Counter: <span id="count">0</span>
  <button id="inc">Increment</button>
</div>

<script>
const channel = new BroadcastChannel('counter');
let count = parseInt(localStorage.getItem('counter') || '0');
document.getElementById('count').textContent = count;

document.getElementById('inc').addEventListener('click', () => {
  count++;
  localStorage.setItem('counter', count);
  document.getElementById('count').textContent = count;
  // Broadcast new value to all other tabs
  channel.postMessage({ type: 'COUNT_UPDATE', value: count });
});

// Receive update from another tab
channel.onmessage = (event) => {
  if (event.data.type === 'COUNT_UPDATE') {
    count = event.data.value;
    document.getElementById('count').textContent = count;
  }
};
</script>

Combine BroadcastChannel with localStorage for both persistence and real-time cross-tab sync.

Common Mistakes

  • Forgetting that the sender does NOT receive its own broadcast update your own UI state separately, then broadcast to others
  • Not calling `channel.close()` when a component unmounts in a SPA listeners accumulate and cause memory leaks
  • Using BroadcastChannel for large data messages are structured-cloned (copied), not shared; for large shared data, use SharedArrayBuffer

Note

For debugging, open the same page in two browser tabs. Changes in tab 1 should immediately appear in tab 2. Chrome DevTools > Application > Background Services > BroadcastChannel shows all messages in real-time.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

Which contexts can communicate via BroadcastChannel?