Broadcast Channel API: tell another tab that its record may be stale

The Broadcast Channel API sends structured-cloneable messages between eligible browsing contexts and workers using the same channel name, origin, and storage partition. Create a BroadcastChannel, call postMessage(), listen for message, and call close() when its owner is finished. It does not store messages for a tab that opens later or synchronize another device. Broadcast Channel API

A document editor needs care when two tabs open the same record. In this original example, tab A saves a corrected access instruction while tab B holds an unsaved contact edit. Tab B should show that a newer saved copy exists while preserving its own words.

Start with the smallest channel connection

In each participating page served from the same application origin:

const channel = new BroadcastChannel('document-records-v1');

channel.addEventListener('message', (event) => {
  console.log('Another participant sent:', event.data);
});

channel.postMessage({ type: 'record-saved', recordId: 'note-42' });

// When the owning component is permanently disposed:
// channel.close();

Use two eligible tabs or two separate channel objects to observe delivery. The sending object does not receive its own message. A different port, protocol, hostname, or channel name separates participants. Storage partitioning can also separate a cross-site embedded frame from a top-level page that otherwise shares its origin.

Messages carry application data. Functions and DOM nodes cannot be structured-cloned. Channel names and payload fields are conventions your application defines; the API does not interpret “saved” or authenticate a record update. The HTML standard defines the channel's delivery behavior.

Announce only a successful application save

Send a record identifier after the existing save service succeeds. The following wrapper receives that service as an application callback:

async function saveAndAnnounce(recordId, draft, persistRecord) {
  const saved = await persistRecord(recordId, draft);
  channel.postMessage({
    type: 'record-saved',
    recordId
  });
  return saved;
}

persistRecord must reject on failure and resolve only after the application's actual save contract succeeds. It owns authentication, record identity, and any server version checks. This fragment defines no endpoint or server implementation. A notification failure after the awaited save must not be presented as proof that persistence failed; report the two outcomes separately in the real application.

The receiving tab should treat this small message as a reason to check the server record, rather than as replacement document content. That keeps a same-browser message from becoming an unchecked write into a local draft.

Preserve a dirty draft when a notification arrives

This bounded listener delegates UI decisions to the existing application:

function watchOpenRecord(channel, recordId, showNewerCopyNotice) {
  const onMessage = ({ data }) => {
    if (!data || data.type !== 'record-saved') return;
    if (data.recordId !== recordId) return;
    showNewerCopyNotice();
  };

  channel.addEventListener('message', onMessage);
  return () => channel.removeEventListener('message', onMessage);
}

showNewerCopyNotice should mark the open record as potentially stale without changing its text. Offer a deliberate action to fetch and inspect the authoritative copy. If the local document has unsaved changes, preserve those changes before any replacement. If the application automatically refreshes a clean record, recheck that it is still clean and still the same record after the request completes.

A message may arrive more than once or after a different save. Treat the notice as repeatable. Avoid replying by rebroadcasting the same notification, which can create an echo loop. Concurrent saves still need the server's conflict policy; BroadcastChannel does not merge two edits or reserve ownership of a record.

Test missing messages as well as received ones

Open note 42 in two application tabs. Make an unsaved contact edit in B, then save the access correction in A. B should show its notice and retain the contact edit. A notification for note 43 should leave note 42 alone. Close B before another save and reopen it afterward: its initial load must fetch the current server record because the channel is not a durable mailbox.

The broadcast graphics example sends graphic state to a separate display. Here the message invalidates an open record, and the saved document remains with the application's persistence service. The storage-event guide covers a different route when the value itself lives in localStorage.

Dispose the listener when its record closes and close the channel when no owner needs it. Finish the two-tab review by comparing the saved access correction with the preserved contact edit, then choose what to keep through the application's normal save and conflict controls.

Keep reading