Master Cross‑Tab Communication: BroadcastChannel vs localStorage in JavaScript
Learn how to synchronize data across multiple browser tabs using the native BroadcastChannel API and the fallback localStorage + storage event, with step‑by‑step code examples, usage scenarios, feature comparisons, compatibility notes, and a reusable TabMessenger class for seamless inter‑tab messaging.
Hello, I'm Shi Xiaoshi, an open‑source contributor and author of the "Tampermonkey Script Practical Guide". While developing Tampermonkey scripts, I needed a way for multiple tabs of the same web app to share messages—e.g., when one tab logs out, all others should log out too.
Instead of the older
GM_saveTabAPI, I discovered that the native
BroadcastChannelAPI and the
localStorage+
storageevent can easily solve cross‑tab data sharing.
Using BroadcastChannel for Tab Communication
What is BroadcastChannel?
BroadcastChannelis a browser‑provided API that enables communication between different tabs of the same origin, acting like a broadcast station where any tab can send a message and all listening tabs receive it.
Example
<code>const channel = new BroadcastChannel('my-channel');</code> <code>channel.onmessage = (e) => {
console.log('Received message:', e.data);
};</code> <code>channel.postMessage('Hello, other tabs!');</code> <code>channel.close();</code>Demo: Real‑time Message Broadcast Across Tabs
<code><!DOCTYPE html>
<html>
<head>
<title>BroadcastChannel Demo</title>
</head>
<body>
<h2>BroadcastChannel Demo</h2>
<input id="msgInput" placeholder="Enter message to broadcast" />
<button onclick="sendMessage()">Send</button>
<ul id="log"></ul>
<script>
const channel = new BroadcastChannel('demo_channel');
const log = document.getElementById('log');
channel.onmessage = (event) => {
const li = document.createElement('li');
li.textContent = `Received: ${event.data}`;
log.appendChild(li);
};
function sendMessage() {
const input = document.getElementById('msgInput');
channel.postMessage(input.value);
input.value = '';
}
</script>
</body>
</html></code>Using localStorage + storage Event for Synchronization
storage Event
When you need to support older browsers or avoid adding BroadcastChannel,
localStoragecan be used for communication. Changing
localStoragein one tab triggers a
storageevent in all other tabs.
Tab A (sender):
<code>localStorage.setItem('laoliu', JSON.stringify({
type: '不想工作',
text: "只要胆子大,天天都是假"
}));</code>Tab B (receiver):
<code>window.addEventListener('storage', (event) => {
if (event.key === 'laoliu') {
const msg = JSON.parse(event.newValue);
if (msg.type === '不想工作') {
alert('6666,你个老六');
}
}
});</code>Demo: localStorage Data Sync
<code><!DOCTYPE html>
<html>
<head>
<title>localStorage + storage event</title>
</head>
<body>
<h2>localStorage Communication Demo</h2>
<input id="msgInput" placeholder="Enter message to sync" />
<button onclick="syncMessage()">Sync</button>
<ul id="log"></ul>
<script>
const log = document.getElementById('log');
function syncMessage() {
const input = document.getElementById('msgInput');
localStorage.setItem('crossTabMessage', JSON.stringify({
text: input.value,
timestamp: Date.now()
}));
input.value = '';
}
window.addEventListener('storage', (event) => {
if (event.key === 'crossTabMessage') {
const data = JSON.parse(event.newValue);
const li = document.createElement('li');
li.textContent = `Received: ${data.text}`;
log.appendChild(li);
}
});
</script>
</body>
</html></code>Key Considerations
The
storageevent fires only in tabs *other* than the one that performed the change.
The sender does not receive its own message; only receivers respond.
Include a unique identifier such as a timestamp in the payload to avoid cache‑related misinterpretations.
Feature Comparison
Both BroadcastChannel and
localStorage+
storagesupport same‑origin communication, but BroadcastChannel offers automatic object serialization, true bidirectional messaging, and a cleaner API, while
localStorageworks in older browsers but requires manual serialization and cannot receive its own messages.
Complete Wrapper Demo
A reusable
TabMessengerclass abstracts the two approaches:
<code>class TabMessenger {
constructor(channelName = 'default') {
this.isBroadcastSupported = typeof BroadcastChannel !== 'undefined';
this.channelName = channelName;
if (this.isBroadcastSupported) {
this.channel = new BroadcastChannel(channelName);
} else {
window.addEventListener('storage', this._onStorage.bind(this));
}
}
onMessage(handler) {
if (this.isBroadcastSupported) {
this.channel.onmessage = (e) => handler(e.data);
} else {
this.storageHandler = handler;
}
}
postMessage(msg) {
if (this.isBroadcastSupported) {
this.channel.postMessage(msg);
} else {
localStorage.setItem('__tab_msg_' + this.channelName, JSON.stringify({
msg,
ts: Date.now()
}));
}
}
_onStorage(e) {
if (e.key.startsWith('__tab_msg_' + this.channelName)) {
const data = JSON.parse(e.newValue);
this.storageHandler?.(data.msg);
}
}
}</code>Usage example:
<code>const messenger = new TabMessenger('myApp');
messenger.onMessage((data) => {
console.log('Received:', data);
});
messenger.postMessage('Hello, other pages!');</code>Conclusion
Without any heavy frameworks, native browser APIs allow effortless data synchronization across tabs. For modern browsers,
BroadcastChannelis the preferred choice due to its simplicity and directness; for legacy support, the
localStorage+
storagefallback remains reliable.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.