Skip to content

BroadcastChannel

BroadcastChannel is a browser API that provides a simple publish/subscribe message bus scoped to the same origin. Any tab, iframe, or worker that creates a channel with the same name automatically joins the same group. When one member calls postMessage, every other member receives the message via its onmessage handler.

It is conceptually similar to localStorage’s storage event, but with important differences:

FeatureBroadcastChannelstorage event
Message contentAny structured-cloneable valueString only (storage value)
Works in workersYes (Shared/Service Workers)No
Fires in the sending tabNoNo
Requires localStorage writeNo — directYes
// Create (or join) a channel by name
const ch = new BroadcastChannel('my-channel');
// Listen for messages
ch.onmessage = (event) => {
console.log('Got:', event.data);
};
// Send a message to all other members
ch.postMessage({ type: 'UPDATE', payload: 42 });
// When done, release resources
ch.close();

The name is the only identifier — two pages on the same origin using new BroadcastChannel('chat') are automatically peers. There is no server or registration step.

You can also use addEventListener instead of the onmessage assignment. This lets you attach multiple listeners:

const ch = new BroadcastChannel('notifications');
ch.addEventListener('message', (e) => {
console.log('Handler 1:', e.data);
});
ch.addEventListener('message', (e) => {
console.log('Handler 2:', e.data);
});

Call ch.close() when you no longer need to receive messages. The channel is not shared state — closing in one tab has no effect on other tabs. If you forget to close it, the browser may keep an event-listener reference alive.

const ch = new BroadcastChannel('demo:temp');
// ... use it ...
ch.close(); // releases internal resources

Runnable: post and receive on the same channel

Section titled “Runnable: post and receive on the same channel”

The demo below creates a channel, attaches a listener, posts a message, and closes after receipt. Because sender and receiver are in the same page the message echoes back — in a real app the listener lives in a separate tab.

Browser Storage
OptionBenefitCost
BroadcastChannelSends a real-time message to every tab/worker that’s joined the same channel, with no need to write to storage firstNo built-in persistence — if no tab is listening when the message is sent, it’s simply lost
storage eventReuses a persistence layer you already have (localStorage) as a side effect, with no extra channel to set upCan only send strings, and never fires in the tab that wrote — you handle serializing/deserializing yourself
window.postMessage across a window referenceFine-grained control over the target — sends only to a window you hold a reference toYou must hold each window’s reference yourself, which gets hard with tabs opening and closing dynamically
  • Assuming BroadcastChannel queues a message for a receiver that joins later — you must join the channel before the message is sent. If the receiving tab hasn’t created its BroadcastChannel yet, the message is silently lost.
  • Forgetting to call ch.close() on component unmount — the channel object and its listener stay in memory, leaking, especially in an SPA that mounts/unmounts often.
  • Expecting the tab that calls postMessage to also receive its own event — BroadcastChannel never delivers a message back to the sender, just as the storage event never fires in the writing tab. Update the sending tab’s own UI separately.
What value types can you pass to BroadcastChannel.postMessage()?
Two tabs on https://example.com both create new BroadcastChannel("sync"). Tab A calls postMessage. Which tabs receive the message event?
What happens when you call ch.close() in one tab?
Which of the following is a key advantage of BroadcastChannel over the localStorage storage event?