Tabsub: Creating a Simple Messaging Library
vorillaz
Posted on April 4, 2019
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.
Demo
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
channel.post('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
- BroadcastChannel at MDN
- BroadcastChannel at Google Developers
- BroadcastChannel polyfill with support for Node.js
- localStorage at MDN
You can also find this post on vorillaz.com
Posted on April 4, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.