Server-Sent Events (SSEs)
One-way server-to-client streaming over plain HTTP, with automatic reconnection built into the browser. The simplest right answer for feeds, progress updates and streaming AI responses.
Server-Sent Events give you a long-lived HTTP response that the server keeps writing to. The client listens; if it wants to send something back, it uses an ordinary request. That one-way restriction sounds limiting, but a surprising number of “real-time” features only need the server to talk.
What it looks like
The server responds with Content-Type: text/event-stream and writes small text frames:
HTTP/1.1 200 OK
Content-Type: text/event-stream
id: 42
event: progress
data: {"pct": 60}
And the browser has a built-in client:
const es = new EventSource("/jobs/123/events");
es.addEventListener("progress", (e) => setProgress(JSON.parse(e.data).pct));
EventSource reconnects automatically and sends the last event ID it saw (Last-Event-ID), so the server can replay what
was missed. With WebSockets you write all of that yourself.
A pattern I like: respond fast, enrich later
A user submits something that takes a while to process — say, an idea that needs analysing. Return a quick response immediately, then stream updates over SSE as background work finishes. The UI feels instant and gets richer as results arrive. It’s also exactly how most LLM chat interfaces stream tokens.
Gotchas
- Text only. Binary has to be encoded.
- No custom headers on
EventSource. Use cookies, or afetch-based stream reader if you need anAuthorizationheader. - Connection limits. Under HTTP/1.1, browsers allow around six connections per domain, which several open tabs can exhaust. HTTP/2 multiplexes streams and makes this go away.
- Buffering proxies can hold the stream back. Disable response buffering, and send a comment line (
: ping) every so often as a Heartbeat so idle timeouts don’t kill it.
Choosing
If the client only needs to receive, start with SSE. Reach for WebSockets when the client also sends frequently on the same channel. SSE is essentially the standardised, browser-supported version of HTTP Long Polling.
- #sse
- #real-time
- #streaming
- #http
↩ Linked from (5)
- AJAX PollingGrowing
…| | HTTP Long Polling | server answers when ready | low | held connections | fallback, legacy, job queues | | Server-Sent Events (SSEs) | server → client | low | one connection per client | feeds, progress, notifications…
- HeartbeatGrowing
…hem up is a classic way to cause a restart loop. Long-lived connections: WebSockets use ping/pong frames, and Server-Sent Events (SSEs) streams send comment lines, both to detect dead peers and to stop proxies culling an…
- HTTP Long PollingGrowing
…send a cursor (?since=…) so the server can replay anything missed. This is the same idea as Last-Event-ID in Server-Sent Events (SSEs). Server resources. Every waiting client holds a connection. That's cheap with async…
- Message PassingGrowing
…ve message handling. Distributed systems: queues, event streams and real-time channels such as WebSockets and Server-Sent Events (SSEs) are message passing between machines.
- WebSocketsEvergreen
…s, order entry) | WebSockets | | Only the server pushes (feeds, notifications, progress, LLM token streams) | Server-Sent Events (SSEs) — simpler, plain HTTP | | Updates are rare or freshness isn't critical | AJAX Pollin…
↪ Links to (3)
Related by topic
- Growingmetaphor
Balance Is a Control Loop
Calisthenics Lab - Growingcase study
Building a Graph Component Library
Frontend craft - Growing
Graph Visualisation Libraries
Real-time UIs - Seedmetaphor
Regress, Don't Grind: Graceful Degradation
Calisthenics Lab