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.
Inhaltsverzeichnis
- 1. Das Multi-Tab-Problem in Webanwendungen
- 2. BroadcastChannel: Grundprinzip und API
- 3. Praxisbeispiel: sicherer Multi-Tab-Logout
- 4. Theme und Einstellungen über Tabs synchronisieren
- 5. Reaktives State-Sharing zwischen Tabs
- 6. Service Worker und BroadcastChannel
- 7. Strukturierte Nachrichten und Protokolldesign
- 8. Fehlerbehandlung und Cleanup
- 9. BroadcastChannel vs. Alternativen
- 10. Zusammenfassung
- 11. FAQ
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.