Lesson 0002 · ~12 minutes

Core API: open, post, listen, close

One skill: wire a named BroadcastChannel correctly — construct, listen, post (with local side effects), and close — including sender-excluded delivery and structured-clone limits.

Win for this lesson

You can sketch a two-tab logout or theme fan-out in a few lines and correctly predict who receives the message — without re-litigating SharedWorker vs BC.

1. Open a named channel

Create a BroadcastChannel with a string name. The name is how peers find each other — same origin partitioning + exact same name = one bus (HTML §9.5).

const auth = new BroadcastChannel("auth");
console.log(auth.name); // "auth" — read-only after construction

// Different name ⇒ different bus (silent isolation, not an error)
const theme = new BroadcastChannel("theme");

2. Listen, then post

Incoming messages are message events on the channel object (a MessageEvent). Use onmessage or addEventListener("message", …). Payload is in event.data; event.origin is the poster’s origin.

const auth = new BroadcastChannel("auth");

auth.onmessage = (event) => {
  if (event.data === "logout" || event.data?.type === "logout") {
    showLoggedOut();
  }
};

function logoutRequested() {
  // 1) Local side effects first — you will NOT get your own message
  doLogout();
  showLoggedOut();
  // 2) Fan-out to other same-origin contexts on this channel
  auth.postMessage("logout");
}

That shape matches the HTML logout example. Objects work too: auth.postMessage({ type: "logout", at: Date.now() }).

Sender excluded

Spec algorithm: build the destination list, then remove the source channel. Other tabs with new BroadcastChannel("auth") hear you; your own onmessage does not fire for that post. Always update local UI (or re-enter a shared handler) yourself.

3. Structured clone — no transfer list

postMessage(message) runs structured serialize only. Unlike worker.postMessage(msg, [transfer]), BroadcastChannel has no transfer parameter (IDL in §9.5). Receivers get a clone, not ownership of a transferable.

If deserialize fails at a destination, messageerror fires there instead of message.

4. Close and lifetime

close() sets the closed flag. Further postMessage on that object throws InvalidStateError. The Standard also notes: leave a channel open with a message listener and it can look like a leak — the global keeps a strong reference while listeners exist (§9.5). Close when a feature unmounts; tab/worker teardown ends participation too.

auth.close();
// auth.postMessage("logout"); // InvalidStateError

// Prefer one long-lived channel per name in an app shell, or close on cleanup:
// useEffect(() => { const ch = new BroadcastChannel("theme"); …; return () => ch.close(); }, []);
Default rule (steal this)

One channel name per concern → onmessage switch on typelocal apply then postclose() when the owner goes away. Keep payloads small; never assume the bus is durable storage.

Call Role
new BroadcastChannel(name) Join / create the named bus for this context
name Read-only channel name
postMessage(message) Clone to other same-name channels; not to self
onmessage / message Receive; data is the clone
onmessageerror Clone/deserialize failure at this destination
close() Detach; further posts throw

5. Practice — predict the API

Equal-length options. Think like a code review on multi-tab messaging.

Scenario A

Tab A posts "logout" on channel "auth". Tab A also has auth.onmessage registered. What should Tab A do for its own UI?

Scenario B

Tab A uses new BroadcastChannel("auth"). Tab B uses new BroadcastChannel("Auth") (capital A). Same origin.

Scenario C

You want to send a large ArrayBuffer to other tabs without copying, the way worker.postMessage(buf, [buf]) transfers ownership.

Scenario D

A React effect closed its BroadcastChannel on unmount. Later code calls postMessage on that same object.

Scenario E

Theme toggle: persist preference and update every open same-origin tab’s chrome. Prefer the lightest correct wiring.

6. What to remember

Ask your teacher Fuzzy bits — workers joining the same channel, React strict-mode double mount, or a two-tab experiment that “doesn’t fire” — ask in chat.

Primary source (read next)

MDN — BroadcastChannel for the interface surface, then the normative steps in HTML §9.5 (destination list, sender removal, serialize, close flag). Keep core-api.html open while you practice.