Lesson 17 of 30
For one-directional server-to-client updates, the browser already does the reconnection work a WebSocket implementation has to hand-roll — the real skill here is knowing when that trade actually applies.
11 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
What's the concrete implementation reason SSE reconnection is genuinely simpler than hand-rolled WebSocket reconnection?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
The previous lesson covered the real implementation work a WebSocket needs from the client: opening it inside a component's lifecycle, closing it correctly, and hand-rolling reconnection with backoff. Server-Sent Events (SSE) are the concrete implementation counterpart for the one-directional case — server pushes to client, client never sends anything back over that same channel — and the client-side API involved is EventSource.
Using it is genuinely simple:
EventSource pointed at a streaming endpoint.message event, or for named custom event types if the server sends them.EventSource implementation retries it on its own — no manual close-detection, no backoff loop to write, none of the reconnection code the WebSocket lesson walked through by hand.That last point is the real, practical payoff of SSE over a hand-rolled WebSocket for this specific shape of problem: reconnection is simpler because it isn't your code's job.
function useNotifications() {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [connectionState, setConnectionState] = useState<"connecting" | "open" | "retrying">("connecting");
useEffect(() => {
const source = new EventSource("/api/notifications/stream");
source.onopen = () => setConnectionState("open");
// The default, unnamed event type.
source.onmessage = (event) => {
const notification: Notification = JSON.parse(event.data);
setNotifications((prev) => [...prev, notification]);
};
// A named custom event the server can send alongside plain messages.
source.addEventListener("alert", (event) => {
const alert = JSON.parse((event as MessageEvent).data);
setNotifications((prev) => [...prev, { ...alert, urgent: true }]);
});
source.onerror = () => {
// The browser is already retrying the connection at this point —
// this handler exists purely to reflect that state in the UI.
setConnectionState("retrying");
};
return () => source.close();
}, []);
return { notifications, connectionState };
}
Even though reconnection happens without any code from you, the error event is still worth listening for. A connection that silently retries with zero UI feedback looks broken to a user during a real outage — they see updates stop arriving with no explanation. Surfacing a "reconnecting" state turns invisible automatic recovery into something the UI can actually communicate.
EventSource genuinely cannot do it — that's the point where a WebSocket, not SSE, is the correct tool.alert event that the client only listens for as message (the default) will silently never trigger the alert handler, because addEventListener("message", ...) and onmessage only catch the unnamed default event type."Would you use SSE or a WebSocket here?" is a common follow-up once a real-time feature comes up, and the strongest answers name the concrete implementation trade directly: SSE's reconnection is simpler because the browser handles it, but that's only available because the problem is one-directional in the first place.