Reference

BroadcastChannel core API

Compressed sheet for open / post / listen / close. Print-friendly.

Construct

const auth = new BroadcastChannel("auth");
// auth.name === "auth"  (read-only; set only at construction)

// Same string = same bus (exact match). Different name = different bus.
const theme = new BroadcastChannel("theme");

Listen

auth.onmessage = (event) => {
  // event.data  — structured-clone copy of what was posted
  // event.origin — origin of the poster
  if (event.data?.type === "logout") showLoggedOut();
};

// or:
auth.addEventListener("message", (event) => { /* … */ });

auth.onmessageerror = (event) => {
  // deserialize failed (rare for ordinary JSON-shaped payloads)
};

Post

// Local work first if this tab must update too — sender is excluded.
doLogoutLocally();
auth.postMessage({ type: "logout" });

// Strings, numbers, plain objects, arrays, many built-ins OK.
// Functions / DOM nodes → DataCloneError (structured clone rules).
// No transfer list — unlike Worker.postMessage(msg, [transfer]).
Who receives?

Other BroadcastChannel objects that are eligible for messaging, share the same storage key / origin partitioning, and have the same channel name. The poster’s own channel is removed from destinations ((HTML §9.5).

Close

auth.close();
// auth.postMessage("x"); // throws InvalidStateError (closed flag)

// Spec: close when done so listeners don’t pin the object (apparent leak).
// Context teardown (tab/worker close) also ends participation.

Minimal multi-tab pattern

// every tab / relevant worker
const ch = new BroadcastChannel("app-sync");

ch.onmessage = (e) => {
  switch (e.data?.type) {
    case "logout":
      clearSessionUI();
      break;
    case "theme":
      applyTheme(e.data.value);
      break;
    case "invalidate":
      refetch(e.data.resource);
      break;
  }
};

function setTheme(value) {
  applyTheme(value); // local — sender excluded
  localStorage.setItem("theme", value); // durable truth if needed
  ch.postMessage({ type: "theme", value });
}

// on page teardown / leave feature
// ch.close();

Gotchas (API layer)

Trap Fix
UI only updates in other tabs Run local handler; then postMessage
Post after close() Throws InvalidStateError — don’t reuse closed channels
Typo in channel name Silent non-delivery — names must match exactly
Huge binary payloads Clone cost; no transfer — keep messages small; store bulk in IDB/OPFS
Cross-origin “same site” Wrong tool — origin must match (partitioning applies)