Lesson 0002 · ~10 minutes

EventSource: the browser contract

One skill: predict connection lifecycle and wire a correct client — open, listen, recover vs stop, close on unmount.

Win for this lesson

You can sketch a React/Vue cleanup-safe client and explain what onerror means by reading readyState — not by guessing.

1. The whole API fits in one breath

EventSource is intentionally small. From the WHATWG interface: construct with a URL, optional withCredentials, read readyState, listen for open / message / error (plus named types), call close() when done.

const es = new EventSource("/api/stream");

es.addEventListener("open", () => {
  console.log("open", es.readyState); // EventSource.OPEN === 1
});

es.onmessage = (e) => {
  // Default wire type: no `event:` field on the server
  console.log(e.data);        // always a string
  console.log(e.lastEventId);
};

es.addEventListener("invoice.paid", (e) => {
  // Only if server sent: event: invoice.paid
  handle(JSON.parse(e.data));
});

es.onerror = () => {
  // See section 3 — readyState tells retry vs dead
};

// SPA unmount / leave page intent:
es.close(); // readyState → CLOSED (2); no more reconnects
Design note for FE leads

Native EventSource does not accept a request body or arbitrary headers. Auth is usually cookies (optionally withCredentials: true cross-origin) or a token in the query string / path. If your platform mandates Authorization: Bearer … on every call, you either change the auth model or stream with fetch + manual parse instead of raw EventSource. Flag that early in design reviews.

2. readyState is the control panel

State Value When
CONNECTING 0 Just constructed, or mid-reconnect after a drop (spec: reestablish path)
OPEN 1 Server answered 200 with Content-Type: text/event-stream; events may flow
CLOSED 2 Fatal failure or you called close() — UA does not reconnect

Spec processing model (short version): announceOPEN + open event; reestablishCONNECTING + error, wait, refetch (may send Last-Event-ID); failCLOSED + error, stop forever.

3. onerror is not one meaning

The same error event fires when the browser is about to retry and when the connection is dead. Always branch on state:

es.onerror = () => {
  if (es.readyState === EventSource.CLOSED) {
    // Fatal: 404, wrong content-type, auth hard-fail, close(), etc.
    showBanner("Live updates stopped");
    return;
  }
  // CONNECTING: transient; browser will retry on its own
  showBanner("Reconnecting…");
};

Useful server signals from the intro: 204 No Content tells the client to stop reconnecting; wrong status or non-text/event-stream fails the connection. (WHATWG intro)

4. Named events vs onmessage

Wire field event: job-progress does not hit onmessage. Default type is message only when the server omits event:. Use addEventListener("job-progress", …) for named types — same as the spec’s add/remove example.

5. Practice A — predict readyState

Equal-length options · pick the state after the step

Step 1

const es = new EventSource("/events"); — no response yet. What is es.readyState?

Step 2

Server returns 200 with Content-Type: text/event-stream and starts writing. State?

Step 3

Stream was open; the TCP connection drops mid-flight. Browser begins reestablish. State while waiting to retry?

Step 4

User navigates away; your effect cleanup runs es.close(). State?

Step 5

Endpoint returns 401 Unauthorized with a JSON body (not an event stream). State after processing?

6. Practice B — design choices

Scenario A

Server sends event: balance-updated with JSON data. How should the client subscribe?

Scenario B

React effect opens EventSource for a dashboard route. What belongs in the effect cleanup?

Scenario C

Product wants a “live updates paused” banner only when the stream will not come back by itself. What do you check?

7. What to remember

Ask your teacher Cookie vs Bearer, CORS, Workers, or “should we use fetch streaming instead?” — ask now while the API is fresh.

Primary source

WHATWG — The EventSource interface + processing model (sections 9.2.2–9.2.3). Skim the IDL and the announce / reestablish / fail definitions. Use MDN EventSource as the readable companion.