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_saveTab API, I discovered that the native BroadcastChannel API and the localStorage + storage event 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
const channel = new BroadcastChannel('my-channel'); channel.onmessage = (e) => {
console.log('Received message:', e.data);
}; channel.postMessage('Hello, other tabs!'); channel.close();Demo: Real‑time Message Broadcast Across Tabs
<!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>Using localStorage + storage Event for Synchronization
storage Event
When you need to support older browsers or avoid adding BroadcastChannel, localStorage can be used for communication. Changing localStorage in one tab triggers a storage event in all other tabs.
Tab A (sender):
localStorage.setItem('laoliu', JSON.stringify({
type: '不想工作',
text: "只要胆子大,天天都是假"
}));Tab B (receiver):
window.addEventListener('storage', (event) => {
if (event.key === 'laoliu') {
const msg = JSON.parse(event.newValue);
if (msg.type === '不想工作') {
alert('6666,你个老六');
}
}
});Demo: localStorage Data Sync
<!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>Key Considerations
The storage event 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 + storage support same‑origin communication, but BroadcastChannel offers automatic object serialization, true bidirectional messaging, and a cleaner API, while localStorage works in older browsers but requires manual serialization and cannot receive its own messages.
Complete Wrapper Demo
A reusable TabMessenger class abstracts the two approaches:
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);
}
}
}Usage example:
const messenger = new TabMessenger('myApp');
messenger.onMessage((data) => {
console.log('Received:', data);
});
messenger.postMessage('Hello, other pages!');Conclusion
Without any heavy frameworks, native browser APIs allow effortless data synchronization across tabs. For modern browsers, BroadcastChannel is the preferred choice due to its simplicity and directness; for legacy support, the localStorage + storage fallback remains reliable.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
