All posts
sserealtime

Server-Sent Events: A Practical Guide for Full-Stack Developers

A practical guide to Server-Sent Events — a simpler alternative to WebSockets for one-way real-time updates from server to client.

SR

Suhail Roushan

August 6, 2026

·
5 min read
·
0 views

WebSockets get reached for by default for anything "real-time," but a large share of real-time use cases only need data flowing one direction — server to client — and for that specific shape, Server-Sent Events is a simpler tool built on plain HTTP, not a separate protocol.

Server-Sent Events (SSE) is a browser API and protocol for a server to push a stream of text-based updates to a client over a single, long-lived HTTP connection. It's unidirectional (server to client only), built on standard HTTP (no protocol upgrade like WebSockets require), and includes built-in automatic reconnection — the browser's EventSource API handles reconnecting on connection drop without you writing that logic yourself.

Why Server-Sent Events Matter (and When to Skip Them)

For use cases that are genuinely one-directional — live notifications, streaming AI-generated text responses, progress updates, live dashboards — SSE is simpler to implement and operate than WebSockets, since it's just HTTP (works through existing infrastructure, proxies, and load balancers without special handling) and the browser handles reconnection automatically.

Skip SSE for use cases that genuinely need bidirectional communication — a chat application where the client also sends messages, a collaborative editor, multiplayer game state — where WebSockets' two-way channel is actually needed rather than SSE's one-way stream plus separate HTTP requests for the other direction.

Getting Started with Server-Sent Events

A server endpoint streaming events:

app.get("/events", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  const interval = setInterval(() => {
    res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
  }, 1000);

  req.on("close", () => clearInterval(interval));
});

Consuming the stream client-side with EventSource:

const eventSource = new EventSource("/events");

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("received:", data);
};

eventSource.onerror = () => {
  console.log("connection error, browser will auto-reconnect");
};

Core Server-Sent Events Concepts Every Developer Should Know

The EventSource API handles reconnection automatically, without any code you need to write — if the connection drops, the browser reconnects on its own after a brief delay, and the server can send a Last-Event-ID header value back to help the client resume from where it left off, rather than losing events during a reconnect.

The event stream format is simple text, with data: fields separated by double newlines, optionally including event: (custom event type) and id: (for resumption) fields — this simplicity is part of SSE's appeal, since there's no binary framing protocol to implement or debug.

event: notification
id: 42
data: {"message": "New comment on your post"}

SSE works over plain HTTP, meaning it benefits from existing infrastructure (works through most proxies and load balancers without special configuration, unlike WebSockets which require protocol upgrade support) — but also means you're bound by typical HTTP connection limits per domain in some browsers, which matters if you're opening many concurrent SSE connections from one page.

Streaming AI-generated text responses (token by token) is a common, well-fitting SSE use case — this is exactly the shape SSE is built for: server continuously pushing incremental updates (text tokens) to a client that isn't sending anything back during the stream.

for await (const chunk of aiStream) {
  res.write(`data: ${JSON.stringify({ token: chunk })}\n\n`);
}

Common Server-Sent Events Mistakes and How to Fix Them

Mistake 1: using SSE for a use case that actually needs bidirectional communication, working around the one-way limitation with awkward combinations of SSE plus separate HTTP requests when WebSockets would fit more naturally. Fix: recognize when bidirectional communication is a genuine requirement and use WebSockets for that case instead.

Mistake 2: not setting the correct response headers (Content-Type: text/event-stream, disabling response buffering/compression that can interfere with streaming), causing events to arrive delayed or batched instead of streamed in real time. Fix: set headers correctly and verify no intermediate proxy or middleware is buffering the response.

Mistake 3: not handling the connection limit per domain in browsers using HTTP/1.1, hitting issues if opening many SSE connections from the same page to the same domain. Fix: consolidate to fewer connections where possible, or ensure your infrastructure supports HTTP/2 (which removes this per-domain connection limit).

When Should You Use SSE Instead of WebSockets?

Use SSE when your real-time need is genuinely one-directional — server pushing updates to the client — and you want the simplicity of plain HTTP with built-in reconnection handling. Use WebSockets when you need true bidirectional, low-latency communication where the client also needs to send frequent messages back through the same persistent connection.

Server-Sent Events in Production

Implement Last-Event-ID handling on the server so reconnecting clients can resume from where they left off rather than missing events during a brief disconnect. Also verify no proxy or load balancer in your infrastructure is buffering the streamed response, since buffering silently defeats the real-time benefit SSE is meant to provide.

If you're building a notification stream, live progress updates, or AI response streaming and reaching for WebSockets by default, SSE is very likely the simpler, better-fitting tool for that one-directional use case.

Related posts

Written by Suhail Roushan — Full-stack developer. More posts on AI, Next.js, and building products at suhailroushan.com/blog.

Get in touch