JavaScript BroadcastChannel Table

PieceWhat it doesField note
new BroadcastChannel(name)A named busSame name + same origin = same bus, every tab joins
postMessage(data)Broadcast to OTHERSStructured clone - sender does NOT hear itself
onmessageReceive broadcastsEvery tab with the channel gets a copy
close()Leave the busTeardown discipline - channels keep the tab in GC games otherwise
vs storage eventPush vs write-notifystorage event needs a WRITE; BC is pure messaging, no storage
vs Web LocksMessaging vs mutexLocks serialize WHO acts; BC tells EVERYONE what happened
vs Service WorkerSame-device vs pushBC: tabs on one device; SW: reach tabs + offline + push
JSON-free payloadsStructured clone insideDates, Maps, typed arrays pass - like postMessage
Reference: the MDN BroadcastChannel reference. BroadcastChannel is a named message bus across same-origin TABS: new BroadcastChannel('app') on every tab, postMessage from one, onmessage in all the others (never the sender). The map of alternatives: the storage EVENT requires a storage write and carries the new value; Web Locks SERIALIZE who acts; BroadcastChannel is pure fire-and-forget messaging with structured-clone payloads - Dates, Maps and typed arrays ride without JSON. Bottom line: theme sync, cache invalidation, logout propagation, multiplayer-by-tab - any 'tell every other tab' job is one channel. Related tools: localStorage table (the storage-event alternative), web locks table (the coordination counterpart), and structuredClone table (the payload rules).

BroadcastChannel is a named message bus across same-origin TABS: new BroadcastChannel('app') on every tab, postMessage from one, onmessage in all the others - and never the sender. It is pure fire-and-forget messaging with structured-clone payloads.

Bottom line: the alternatives map picks the tool. The storage EVENT requires a storage write and carries values; Web Locks SERIALIZE who acts; BroadcastChannel just TELLS everyone - theme changes, cache invalidation, logout propagation, tab-multiplayer. No storage side effects, no coordination, one channel.

The honest part: channels are same-origin, same-device, same-browser - like Web Locks, they stop at the browser process. And they are not a queue: a tab opened after the broadcast missed it forever. Durable delivery needs storage or a server.

How to use

  1. Sync settings instantly: const bc = new BroadcastChannel('theme'); bc.postMessage({ theme: 'dark' }) - every open tab re-themes in the same tick-ish.
  2. Propagate logout: bc.postMessage({ type: 'logout' }) from the tab that logged out - every other tab clears state and returns to login.
  3. Invalidate caches: after a data mutation, postMessage({ type: 'invalidate', key }) - other tabs refetch instead of showing stale lists.

Frequently asked questions

When is BroadcastChannel the right choice over the storage event?

Intent versus side effect. The storage event fires in OTHER tabs when localStorage CHANGES - it works, but it conflates persistence with messaging: you must write a dummy value to broadcast, read the value to know what happened, and filter events for keys you care about. BroadcastChannel is messaging PURE: named channels, arbitrary structured-clone payloads (Dates, Maps, typed arrays - storage only ever carries strings), no storage side effects, and no serializing through a string. The storage event retains one advantage: it works even when the message should ALSO persist, and it fires in tabs across the origin without pre-arranged channel names. For 'tell tabs something happened', BC; for 'the stored value changed', storage event.

How does BroadcastChannel compare with Web Locks and Service Workers?

Three different cross-tab jobs. BroadcastChannel = ANNOUNCEMENT: fire-and-forget to everyone listening, no ordering guarantees, no durability. Web Locks = COORDINATION: serialize who acts, block others while one holds - it says nothing to bystanders. Service Worker = INFRASTRUCTURE: reaches tabs plus offline and push, and its messaging is routed (postMessage to specific clients). The composable pattern: a Web Lock elects a leader tab, the leader fetches, and BroadcastChannel announces the result to every tab - each primitive doing its one job. Choosing by verb: 'tell everyone' = BC; 'only one of us may act' = locks; 'reach tabs I cannot see, or none at all' = service worker.

What are the delivery guarantees I do NOT get?

No durability, no ordering across channels, no self-delivery, no history. A tab opened AFTER a broadcast never sees it - announcements are for tabs that exist NOW (the workaround: pair BC announcements with a state read from localStorage/IndexedDB on startup - announce the change, persist the truth). Two rapid broadcasts may arrive at different tabs in different orders relative to other messages. And the sender never hears its own postMessage - which is usually right, but means code that expects to process its own events needs to call its handler directly too. For guaranteed ordered delivery, you need a real message queue - on the server.

Why does the teardown discipline matter for channels?

A closed-or-not channel is a lifetime question. An unclosed BroadcastChannel keeps its tab receiving messages indefinitely - which is usually what you want while the tab lives, but in long-lived SPAs with login/logout flows it means the logged-out tab still hears private broadcasts (the channel does not know the user left; close() on logout is the fix). The pattern mirrors event listeners: create the channel per component lifetime, close() on teardown, and re-open on re-login with fresh handlers. Channels are cheap to create - per-session, per-feature channels ('auth', 'theme', 'data') beat one global channel whose every listener filters every message type.

Related tools