JS
() =>
JavaScript · Web APIs · Multi-Tab · Service Worker
Broadcast Channel API
Tabs in Echtzeit synchronisieren – ohne Server

Nutzer öffnen Web-Apps in mehreren Tabs gleichzeitig. Die Broadcast Channel API löst elegantly das Synchronisationsproblem: Logout, Theme-Wechsel, Session-Updates und State-Änderungen werden sofort in alle offenen Tabs gesendet – komplett ohne Server, ohne WebSocket, ohne Polling.

11 Min. Lesezeit BroadcastChannel · postMessage · Service Worker · localStorage Chrome 54+ · Firefox 38+ · Safari 15.4+ · Node.js 15.4+

1. Das Multi-Tab-Problem in Webanwendungen

Moderne Web-Apps werden von Nutzern häufig in mehreren Browser-Tabs gleichzeitig geöffnet. Das führt zu einem klassischen Synchronisationsproblem: Wenn der Nutzer sich in Tab A abmeldet, bleibt Tab B weiterhin eingeloggt und zeigt private Daten an. Wenn der Nutzer seinen Warenkorb in Tab A aktualisiert, sieht Tab B den alten Warenkorb. Wenn eine Sitzung abläuft, zeigen einige Tabs noch aktive Sessions an, andere bereits Fehlermeldungen. Ohne Kommunikation zwischen Tabs sind diese Inkonsistenten unvermeidbar.

Die traditionellen Lösungsansätze haben alle Nachteile. localStorage-Events (das storage-Event) werden nur ausgelöst wenn ein anderer Tab schreibt – nicht im schreibenden Tab selbst – und haben kein strukturiertes Nachrichtenprotokoll. SharedWorker als zentrale Kommunikationsinstanz ist mächtiger, aber deutlich komplexer. Server-Sent Events oder WebSockets erfordern Server-Infrastruktur für ein Problem, das rein clientseitig ist. Die Broadcast Channel API ist die direkte, native Lösung: ein benannter Kanal, auf dem beliebig viele Browser-Kontexte derselben Origin Nachrichten empfangen und senden können.

2. BroadcastChannel: Grundprinzip und API

Die Broadcast Channel API ist einfach: Man erstellt einen Channel mit einem Namen, registriert einen onmessage-Handler, und kann auf demselben Channel mit postMessage() Nachrichten senden. Alle anderen Browsing-Kontexte derselben Origin, die einen Channel mit demselben Namen offen haben, empfangen die Nachricht – sofort, synchron innerhalb desselben Ereignisschleifendurchlaufs des empfangenden Kontexts. Der sendende Kontext empfängt seine eigene Nachricht nicht zurück.

Der Name des Channels ist die einzige Form der Adressierung. Tabs auf https://app.mironsoft.de und https://www.mironsoft.de teilen keinen Channel, auch wenn beide denselben Channel-Namen verwenden – Origins sind verschieden. Tabs auf https://app.mironsoft.de/page1 und https://app.mironsoft.de/page2 teilen denselben Channel, weil die Origin identisch ist. Ein BroadcastChannel muss explizit mit channel.close() geschlossen werden, wenn er nicht mehr benötigt wird, um Memory Leaks zu vermeiden.


// Tab A and Tab B share the same origin — they communicate via named channel
// --- Tab A (sender) ---
const channelA = new BroadcastChannel('app-events');

// Send structured message — any serializable value works
channelA.postMessage({
  type: 'USER_LOGGED_OUT',
  userId: 'u-123',
  timestamp: Date.now(),
});

// Tab A does NOT receive its own message

// --- Tab B (receiver — any other tab on same origin) ---
const channelB = new BroadcastChannel('app-events'); // same name = same channel

channelB.onmessage = (event) => {
  const { type, userId, timestamp } = event.data;
  console.log(`Received event: ${type} for user ${userId}`);

  if (type === 'USER_LOGGED_OUT') {
    // Redirect or clear state in this tab
    window.location.href = '/login';
  }
};

// Always close channels when done to prevent memory leaks
window.addEventListener('beforeunload', () => {
  channelA.close();
  channelB.close();
});

3. Praxisbeispiel: sicherer Multi-Tab-Logout

Der häufigste und kritischste Anwendungsfall für die Broadcast Channel API ist die Logout-Synchronisation. Wenn ein Nutzer sich in einer Unternehmensanwendung abmeldet, müssen alle offenen Tabs sofort reagieren: Privaten Daten ausblenden, Formulare leeren, zur Login-Seite weiterleiten. Ein Tab, der nach dem Logout noch Zugriff auf sensitive Daten zeigt, ist ein Sicherheitsproblem. Mit der Broadcast Channel API ist das in wenigen Zeilen lösbar.

Ein robustes Logout-System mit BroadcastChannel sendet beim Logout nicht nur ein einfaches Signal, sondern ein strukturiertes Objekt mit Typ, Zeitstempel und optionalem Grund. Der Empfänger prüft den Typ, bereinigt den lokalen State (Zustandsmanagement zurücksetzen, localStorage-Einträge löschen, laufende Requests abbrechen), und leitet dann weiter. Der gleiche Mechanismus funktioniert für Session-Timeouts: Der Service Worker, der periodisch die Session-Gültigkeit prüft, sendet ein Session-Expired-Event auf dem BroadcastChannel, und alle Tabs reagieren gleichzeitig.


// auth-sync.js — Multi-tab authentication synchronization
const AUTH_CHANNEL = 'auth-events';

class AuthSync {
  #channel;
  #handlers = new Map();

  constructor() {
    this.#channel = new BroadcastChannel(AUTH_CHANNEL);
    this.#channel.onmessage = this.#handleMessage.bind(this);
    this.#channel.onmessageerror = (e) => console.error('BroadcastChannel error:', e);
  }

  // Send logout to all other tabs
  broadcastLogout(reason = 'user_initiated') {
    this.#channel.postMessage({
      type: 'LOGOUT',
      reason,
      timestamp: Date.now(),
    });
    // Also perform logout in current tab
    this.#performLogout(reason);
  }

  // Register typed event handlers
  on(type, handler) {
    this.#handlers.set(type, handler);
    return this;
  }

  #handleMessage({ data }) {
    const handler = this.#handlers.get(data.type);
    if (handler) handler(data);
  }

  #performLogout(reason) {
    sessionStorage.clear();
    localStorage.removeItem('auth_token');
    window.location.replace(`/login?reason=${reason}`);
  }

  destroy() {
    this.#channel.close();
  }
}

// Usage
const authSync = new AuthSync();
authSync.on('LOGOUT', ({ reason }) => {
  console.log('Logout received from another tab:', reason);
  sessionStorage.clear();
  window.location.replace('/login?reason=remote_logout');
});

4. Theme und Einstellungen über Tabs synchronisieren

Ein weiterer klassischer Anwendungsfall ist die Synchronisation von Benutzereinstellungen. Wenn ein Nutzer in Tab A den Dark Mode aktiviert, sollen alle anderen offenen Tabs derselben App sofort auf Dark Mode wechseln – ohne Seitenreload, ohne Server-Roundtrip. Das gleiche gilt für Spracheinstellungen, Schriftgrößen, Dashboard-Layouts und andere Preferences, die in Echtzeit angewendet werden sollen.

Das BroadcastChannel-Pattern für Einstellungen ist bidirektional: Wenn Tab A eine Einstellung ändert, sendet er sie auf dem Channel. Alle anderen Tabs empfangen die Änderung und wenden sie an. Tab A selbst wendet die Änderung direkt an (sendet sie nicht an sich selbst). Das ist effizienter als der Storage-Event-Ansatz, der immer erst in localStorage schreiben muss, bevor andere Tabs das Event empfangen. Mit BroadcastChannel ist die Nachricht direkt im Arbeitsspeicher übertragen – kein Storage-Roundtrip.

5. Reaktives State-Sharing zwischen Tabs

Komplexere Anwendungen – insbesondere Kollaborations-Tools, Dashboards und E-Commerce-Anwendungen – profitieren von einer tiefergehenden State-Synchronisation über Tabs. Statt einzelner Events sendet man State-Patches oder State-Snapshots über den BroadcastChannel. Ein Nutzer legt ein Produkt in den Warenkorb in Tab A – Tab B sieht sofort die aktualisierte Warenkorb-Anzeige im Header, ohne Server-Polling.

Ein belastbares State-Sharing-Protokoll über BroadcastChannel sendet delta-Objekte statt vollständiger State-Kopien, um Bandbreite zu sparen. Neue Tabs, die sich öffnen und einem Channel beitreten, können keinen initialen State empfangen (der Channel hat keine History). Dieses Problem löst man, indem ein neuer Tab ein REQUEST_STATE-Event sendet und ein bestehender Tab mit dem aktuellen State antwortet. Dieses Handshake-Muster macht State-Sharing über BroadcastChannel robust auch für spät beitretende Tabs.


// State synchronization with late-join support via REQUEST_STATE handshake
const STATE_CHANNEL = 'app-state';
const channel = new BroadcastChannel(STATE_CHANNEL);

let localState = { cart: [], theme: 'light', notifications: 0 };

// New tab requests state snapshot from existing tabs
channel.postMessage({ type: 'REQUEST_STATE' });

channel.onmessage = ({ data }) => {
  switch (data.type) {
    case 'REQUEST_STATE':
      // Respond with current state — one existing tab will answer
      channel.postMessage({ type: 'STATE_SNAPSHOT', state: localState });
      break;

    case 'STATE_SNAPSHOT':
      // Only apply if we don't have state yet (first snapshot wins)
      if (!localState._initialized) {
        localState = { ...data.state, _initialized: true };
        renderApp(localState);
      }
      break;

    case 'STATE_PATCH':
      // Apply partial update from another tab
      localState = { ...localState, ...data.patch };
      renderApp(localState);
      break;
  }
};

// Dispatch state update — applies locally AND broadcasts to other tabs
function updateState(patch) {
  localState = { ...localState, ...patch };
  renderApp(localState);
  channel.postMessage({ type: 'STATE_PATCH', patch });
}

function renderApp(state) {
  // Re-render relevant parts of the UI
  document.body.dataset.theme = state.theme;
}

6. Service Worker und BroadcastChannel

Die Broadcast Channel API funktioniert nicht nur zwischen Tabs, sondern auch zwischen Service Workern und den Tabs, die sie kontrollieren. Das eröffnet leistungsstarke Szenarien: Ein Service Worker kann Push-Notifications empfangen und relevante Daten direkt über den BroadcastChannel an alle offenen Tabs weiterleiten, ohne jeden Tab einzeln via clients.matchAll() zu adressieren. Das ist einfacher und wartbarer als die alternative Client.postMessage()-API.

Ein praktisches Beispiel: Der Service Worker empfängt ein Push-Event (eine neue Nachricht für den Nutzer), aktualisiert den Cache, und sendet dann ein Event auf dem BroadcastChannel. Alle offenen Tabs empfangen das Event und aktualisieren ihren Unread-Counter oder zeigen einen In-App-Toast. Ohne BroadcastChannel müsste der Service Worker alle Clients aufzählen und jeden einzeln benachrichtigen – mehr Code, mehr Fehlerquellen, gleiche Funktionalität.

7. Strukturierte Nachrichten und Protokolldesign

Der häufigste Fehler bei BroadcastChannel-Implementierungen ist fehlendes Nachrichtenprotokoll. Ohne klare Typisierung werden Empfänger zu monolithischen Switch-Statements mit magischen Strings, die niemand mehr versteht. Ein robustes Protokoll definiert alle Nachrichtentypen als Konstanten oder TypeScript-Discriminated-Unions, schließt immer einen Zeitstempel und eine optionale Sender-ID ein, und versioniert das Protokoll wenn sich das Format ändert.

Der Versand von großen Objekten über BroadcastChannel ist möglich – der Browser nutzt intern den Structured Clone Algorithm, der TypedArrays, Map, Set, Date und andere komplexe Typen korrekt kopiert. Funktionen und DOM-Elemente können jedoch nicht übertragen werden. Für sehr große Daten (z.B. Bilder oder große Arrays) ist SharedArrayBuffer die bessere Wahl, da BroadcastChannel die Daten kopiert statt teilt. Das BroadcastChannel-Protokoll sollte daher auf Steuernachrichten und kleine Datenpakete beschränkt bleiben.

8. Fehlerbehandlung und Cleanup

Die Broadcast Channel API hat ein onmessageerror-Event, das ausgelöst wird, wenn eine Nachricht empfangen wird, die nicht deserialisiert werden kann – etwa wenn das Structured Clone Algorithm auf einen nicht klonbaren Typ trifft. In der Praxis ist das selten, aber es sollte behandelt werden, insbesondere wenn verschiedene Tabs oder Service Worker unterschiedliche Code-Versionen haben könnten.

BroadcastChannel-Instanzen halten einen offenen Port im Browser. Vergisst man, channel.close() aufzurufen, bleibt der Port offen – auch nach dem Entladen der Seite für kurze Zeit. In Single-Page-Applications muss man besonders auf Cleanup achten: In React-Komponenten schließt man den Channel in der useEffect-Cleanup-Funktion. In Vue-Komponenten in onUnmounted. Das Event beforeunload ist eine Sicherheitsnetz-Option, aber keine verlässliche Cleanup-Methode, da es in manchen Browsern nicht zuverlässig gefeuert wird.

9. BroadcastChannel vs. Alternativen

Die Wahl der richtigen Tab-Kommunikationsmethode hängt vom Anwendungsfall ab. Die Broadcast Channel API ist die modernste und direkteste Lösung für die meisten Szenarien. Sie ist einfacher als SharedWorker, flexibler als localStorage-Events und erfordert keine Server-Infrastruktur wie WebSockets.

Methode Empfänger Einschränkungen Empfehlung
BroadcastChannel Alle Tabs, SW, iframes (same-origin) Nur same-origin Standard für Tab-Sync
localStorage storage-Event Andere Tabs (nicht Sender) Nur Strings, Sender excluded Legacy – BroadcastChannel bevorzugen
SharedWorker Alle Tabs (zentral) Komplexer, kein Safari bis 16 Wenn zentraler State nötig
WebSocket Server + alle Clients Server-Infrastruktur nötig Wenn Server-Push nötig
Service Worker postMessage Einzelne Clients Komplexes Client-Management BroadcastChannel aus SW heraus

Die Broadcast Channel API ist in allen modernen Browsern verfügbar (Chrome 54+, Firefox 38+, Safari 15.4+) und in Node.js seit Version 15.4. Für Projekte, die noch ältere Safari-Versionen unterstützen müssen, ist ein Polyfill oder der Fallback auf localStorage-Events notwendig. In der Praxis sind Safari-Versionen unter 15.4 (erschienen Oktober 2021) in professionellen Webanwendungen selten noch eine relevante Zielgruppe.

Mironsoft

Frontend-Architektur, Multi-Tab-State und Web-App-Entwicklung

Ihre Web-App mit Multi-Tab-Synchronisation absichern?

Wir implementieren robuste Tab-Synchronisation mit Broadcast Channel API für Logout, Session-Management und Echtzeit-State-Updates – ohne WebSocket-Overhead.

Security-Audit

Analyse auf Multi-Tab-Sicherheitslücken: Logout ohne Tab-Sync, Session-Leaks in anderen Tabs

Implementation

BroadcastChannel-Integration für Auth, State-Sync und Echtzeit-Einstellungen in React oder Vue

Service Worker

Service Worker mit BroadcastChannel verbinden für Push-Notifications und Offline-Sync

10. Zusammenfassung

Die Broadcast Channel API löst das Multi-Tab-Synchronisationsproblem mit minimalem Aufwand: Ein benannter Channel, ein postMessage()-Aufruf, ein onmessage-Handler. Alle Browser-Kontexte derselben Origin – Tabs, iframes, Service Worker – empfangen Nachrichten sofort. Kein Server, kein WebSocket, kein Polling. Die API ist in allen modernen Browsern verfügbar und seit Node.js 15.4 auch serverseitig einsetzbar. Der Structured Clone Algorithm ermöglicht den Transfer komplexer Objekte – keine JSON-Serialisierung notwendig.

Die wichtigsten Anwendungsfälle sind Logout-Synchronisation (Sicherheitskritisch), Theme und Einstellungs-Sync (UX-Qualität), Session-Timeout-Benachrichtigungen (Session-Management) und reaktives State-Sharing (Kollaborations-Features). Für State-Sharing mit Late-Join-Support implementiert man ein REQUEST_STATE/STATE_SNAPSHOT-Handshake-Protokoll. Channel.close() nicht vergessen – jede BroadcastChannel-Instanz muss explizit geschlossen werden. Mit diesen Grundlagen ist die Broadcast Channel API ein mächtiges Werkzeug für professionelle Multi-Tab-Web-Anwendungen.

Broadcast Channel API — Das Wichtigste auf einen Blick

Grundprinzip

new BroadcastChannel('name') öffnet einen Kanal. postMessage() sendet an alle anderen gleichen Origin. onmessage empfängt. close() aufräumen.

Reichweite

Alle Tabs, iframes und Service Worker derselben Origin empfangen Nachrichten. Cross-Origin funktioniert nicht. Sender empfängt seine eigene Nachricht nicht.

Hauptanwendungsfälle

Logout-Synchronisation (Sicherheit), Theme/Einstellungen-Sync (UX), Session-Timeout-Propagation, reaktives State-Sharing zwischen Tabs.

Browser-Support

Chrome 54+, Firefox 38+, Safari 15.4+, Node.js 15.4+. Für ältere Safari-Versionen: Polyfill oder localStorage-Events als Fallback.

11. FAQ: Broadcast Channel API

1Was ist die Broadcast Channel API?
Native Browser-API für Echtzeit-Kommunikation zwischen Tabs, iframes und Service Workern derselben Origin. Benannter Kanal, postMessage() zum Senden, onmessage zum Empfangen.
2Cross-Domain-Kommunikation möglich?
Nein — nur same-origin. Gleiches Protokoll, gleicher Host, gleicher Port. Für Cross-Origin: window.postMessage() mit explizitem targetOrigin.
3Empfängt der Sender seine Nachricht?
Nein. Der sendende Kontext empfängt seine eigene Nachricht nicht. Lokal anwenden + auf Channel senden sind immer zwei explizite Schritte.
4Unterschied zu localStorage storage-Event?
BroadcastChannel: direktes Senden, komplexe Objekte, kein Storage-Schreibvorgang. localStorage-Event: Umweg über Storage, nur Strings, schreibender Tab empfängt nicht.
5Channel schließen?
channel.close() – in React useEffect Cleanup, in Vue onUnmounted. Nicht geschlossene Channels führen zu Memory Leaks.
6BroadcastChannel mit Service Worker?
Ja – Service Worker können senden und empfangen. Einfacher als clients.matchAll() + Client.postMessage() wenn alle Tabs gleichzeitig informiert werden sollen.
7Welche Daten kann man senden?
Alles Structured-Clone-fähige: primitiv, Array, Object, Map, Set, Date, TypedArray. Nicht: Funktionen, DOM-Elemente, Symbols.
8Neuer Tab bekommt aktuellen State?
BroadcastChannel hat keine History. Neuer Tab sendet REQUEST_STATE, bestehender Tab antwortet mit STATE_SNAPSHOT. Handshake-Pattern löst Late-Join-Problem.
9BroadcastChannel in Node.js?
Seit Node.js 15.4 global verfügbar. Kommuniziert zwischen Worker Threads des gleichen Prozesses – nicht zwischen verschiedenen Node.js-Prozessen.
10Browser-Support?
Chrome 54+, Firefox 38+, Safari 15.4+ (Oktober 2021), Edge 79+. Kein IE. Für ältere Safari: Polyfill oder localStorage-Events Fallback.