Dec 22

BroadcastChannel API

Keep state in sync and communicate across same origin tabs with this native web API.

By Stephanie Eckles

State syncing can be tricky to accomplish with web apps, but it's one of the scenarios improved by using the BroadcastChannel API.

This native web API allows messaging between browsing contexts from the same origin. In other words, if a user has multiple tabs, windows, and even an iframe open from santas-list.tld, use of this API can push messages between all of those contexts. How you handle those messages enables state syncing or other actions.

Sending and Receiving Broadcast Messages

We're going to create a simple app for Santa and his elves to track children's naughty or nice status.

The first step is to set up a broadcast channel. You can name this whatever fits the purpose of your app, and depending on your needs, you can have one channel or multiple.

const santasList = new BroadcastChannel("santasList");

Next, we can send our first message using the API's postMessage method. The content can be a string, as shown, or another data type, like an array or object.

santasList.postMessage("Hello World");

We can receive and then respond to the message by adding an event listener to the broadcast channel we set up, listening to the message event. Any information contained in the message is available in the data property.

santasList.addEventListener("message", ({ data }) => {
console.log(data);
});

We'll wrap that up in a simple button to test it out. You can do this by opening this article again in a new tab. Then, try pressing the button in one tab and then returning to the other tab to see the message output.

Hello World test message
const santasList = new BroadcastChannel("santasList");

document.getElementById('hello-world').addEventListener('click', () => {
santasList.postMessage("Hello World");
});

santasList.addEventListener('message', ({ data }) => {
const messageBox = document.getElementById('hello-world-message');

messageBox.textContent = data;
});

A couple of things to note:

Demo App: Santa's List

For our example, a list of names is presented with options for a "Nice" or "Naughty" behavior. To enable Santa and the elves to check the list twice, we must keep the behavior state in sync across browsing contexts.

In other words, if Priscilla's behavior is updated from "Nice" to "Naughty," we want that immediately reflected in any other open tabs to prevent duplicate updates and stale data.

Preview of the Santa's List app showing the first two names where Priscilla Lloyd is marked "Naughty" and Reece Zavala is marked "Nice"

The demo uses all the same methods we've reviewed. However, the logic is extended to update the button state instead of outputting the message.

This app—even as simple as it is—shows just how flexible the API is since you can send data in any shape, and it's entirely up to your application what you do with that data.

You'll see a little extra fluff in the CodePen because I chose to create a web component for managing each list item to isolate sending and receiving messages per item.

The basic logic is that once either behavior is selected for a list item, its aria-pressed state is toggled. The alternate button is also toggled to the opposite state since the behaviors are mutually exclusive binary choices.

Coupled with the name data and the id ('nice' or 'naughty') of the pressed button, the following message is broadcast.

santasList.postMessage({
name,
id,
pressed // updated aria-pressed state
});

A custom event is used to pass the data from the message event listener. This enables a single message event listener rather than instantiating one per custom element. In the future, we might have additional actions we want to take upon a received message.

const updateListEvent = (detail) =>
document.dispatchEvent(new CustomEvent("update-list", { detail }));

santasList.addEventListener("message", ({ data }) => {
const { name, id, pressed } = data;
updateListEvent({ name, id, pressed });
});

The list item listens for the custom event and checks if its name data matches, and if so, hands off the data to update the corresponding button state for itself.

document.addEventListener("update-list", ({ detail }) => {
if (detail.name !== name) return;

// `handleListUpdate` updates button state from
// both in-page button presses and broadcasts
handleListUpdate(detail.id, detail.pressed);
});

Here's the CodePen putting it together. Again, to see it in action, duplicate this tab to see the state reflected in both.

See the CodePen.

Note that since there's no actual data service behind this simple example, and remembering that the BroadcastChannel in and of itself does not persist data, changes made before opening a new tab will not be reflected. This means that, ultimately, you are likely to need some sort of database or storage solution if data changes need to be saved.

Other Opportunities for Use

Here are a few ways to use the BroadcastChannel API.

Syncing of all kinds of states, including:

You can also use it to take more advanced and involved actions. I became familiar with the API by using it for my custom conference presentation framework (example presentation). I used it to navigate slides from a "notes" view via key presses. I also used messages to trigger custom events to mimic element states like hover and focus, reflect typed text updates, and resize code vs. result panels.

Resources

Check out the following to learn more about the BroadcastChannel API.

Stephanie Eckles

Stephanie Eckles

Stephanie Eckles is a Senior Design Engineer for Adobe Spectrum CSS. She's also the author of ModernCSS.dev which provides modern solutions to old CSS problems as in-depth articles. Steph has 15+ years of webdev experience that she enjoys sharing as an author, workshop instructor, and conference speaker. She's an advocate for accessibility, scalable CSS, and web standards. Offline, she's mom to two girls and a cowboy corgi and enjoys baking.

Stephanie selected The Trevor Project for an honorary donation of $50

The Trevor Project

The Trevor Project’s mission is to end suicide among LGBTQ young people.