There are lots of cases where developers want to post or listen to messages within an application. Posting messages across different browser contexts can vastly improve performance and user experience. Displaying queues with messages and pop notifications, synchronizing stale web views or even simultaneously logging out users from all active windows are different use cases for this method.


#The Broadcast Channel API

The Broadcast Channel API was introduced as a native bus messaging interface for modern browsers. It allows communication between different documents (in different windows, tabs, service workers, web workers, frames or iframes) of the same origin. Messages are broadcasted via a message event fired at all BroadcastChannel objects listening to the channel.

Using the BroadcastChannel API is as simple as creating a publisher and a subscriber listening to the same channel.

// subscriber const channel = new BroadcastChannel('radio-channel'); channel.onmessage = ({ data }) => { const msg = JSON.stringify({ data }); console.log(msg); }; // publisher const channel = new BroadcastChannel('radio-channel'); channel.postMessage('this is a message'); channel.postMessage('This is a another one');

You can play around with this example in JSFiddle.

The BroadcastChannel API also exposes lots of information about the publisher, the source and the origin of the message, as everything is packed into the event that’s published.

#Using localStorage as an elegant fallback

Although the BroadcastChannel API is well supported, you can use the localStorage API to provide better support for older browsers. When new items are added to the storage bucket, subscribers can be notified for updates.

A simple example using the localStorage looks like this:

const channelName = 'radio-channel'; const post = msg => window.localStorage.setItem( channelName, JSON.stringify({ date: new Date(), channelName, msg }) ); // Subscriber window.addEventListener('storage', (data = {}) => { const { key = '__GIBBERISH__', newValue = '{}' } = data; if (key === channelName) { const value = JSON.parse(newValue); const { msg } = value; callback(`Message received: ${msg}`); } }); // publisher post('This is a message'); post({ id: 1, foo: 'bar' });

#Bringing everything together

The BroadcastChannel strategy for messaging can also be combined with the localStorage strategy, which can be used as a fallback. Checking if the BroadcastChannel is supported is as easy as:

const isBroadcastSupported = window && window.BroadcastChannel;

#Introducing Tabsub

In order to reduce friction and create a solid solution to achieve internal communication, I have combined the examples above into tiny (~500 bytes) library called tabsub. The API is lean and minimal and the library can be used right away.

import radio from 'tabsub'; const channel = radio('channel-name'); // Post to channel'this is a message'); // Subscribe channel.on(msg => { console.log(`Received: ${msg}`); }); // Stop listening for a while channel.stop(); // Resume listening for messages channel.start(); // Close the channel channel.close();

#Further resources