Server-Sent Events
Server-Sent Events is a one-way realtime delivery model in which the browser opens a single HTTP connection with EventSource and the server pushes events into that response over time. It uses the `text/event-stream` format, and while the connection stays open the server can keep sending `data`, `event`, `id`, and `retry` fields. When information only needs to flow from server to client -- such as notifications or progress updates -- it lets the UI refresh immediately without repeated polling.
βΆArchitecture Diagram
π ProcessDashed line animations indicate the flow direction of data or requests
Screens that need to learn about server changes quickly are inefficient with request-response alone. If the browser keeps asking every few seconds whether anything changed, the same request keeps generating network traffic and server work even when nothing happened. If the interval is too long, the user sees a completed job or a new alert much later than it actually occurred. What is needed is a channel that lets the server push changes as they happen without the browser having to keep asking. For notifications, progress updates, and live feeds that only flow from server to client, responsiveness drops quickly without that kind of channel.
The early web was built around short requests and responses for documents, so the model was sufficient at first. But as notification feeds, dashboards, and job progress screens became common, the need grew for a way to keep the same HTTP connection open while the server trickled data to the browser. Long polling was a temporary workaround, but it still paid the cost of opening and closing connections repeatedly. Server-Sent Events standardized the EventSource API and the `text/event-stream` format as a stable way to provide one-way realtime streaming on top of HTTP.
When the browser opens a URL with EventSource, the server does not finish the response. Instead, it sets `Content-Type: text/event-stream` and keeps the connection alive. The server then sends each event as a series of lines using `data` and a blank line, and the browser dispatches those payloads as `message` or custom events. Because the connection stays open once it is established, the browser automatically tries to reconnect if the stream drops. If the server sends an `id`, the client can resume from the last received event, and `retry` can control the reconnection interval. The important constraint is that the channel only flows from server to client. The browser cannot send commands on the same connection, so any client-side action still needs a separate request.
Keep a stream open and push events from the server
export function GET() {
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
controller.enqueue(encoder.encode("retry: 3000\n"));
controller.enqueue(encoder.encode("event: progress\n"));
controller.enqueue(encoder.encode("data: {\"step\":1}\n\n"));
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
});
}An `text/event-stream` response stays open instead of ending. The `data:` line plus the blank line form one event.
The browser receives events through EventSource
const source = new EventSource("/events");
source.addEventListener("progress", (event) => {
const data = JSON.parse((event as MessageEvent).data);
console.log(data.step);
});
source.onerror = () => source.close();The client does not poll manually. EventSource receives the events from the open stream and dispatches them automatically.
SSE and WebSocket are both long-lived connections, but SSE only flows from server to client, while WebSocket sends messages in both directions. The direction of writing is the core difference even when the realtime feel is similar. SSE is also often compared with REST. REST ends one response per request, while SSE keeps a single response open and continues sending events. The deciding factor is whether changes arrive continuously and need to be pushed as they happen.
Commonly Compared Concepts
WebSocket
A real-time channel that keeps a connection open after HTTP and exchanges messages in both directions
SSE and WebSocket both keep a connection open, but SSE only flows from server to client while WebSocket exchanges messages in both directions. The direction of writing is the key difference.
REST
An API design style that works with resources using HTTP methods and URIs
REST ends one response per request, while SSE keeps a single response open and continues sending events. Whether changes arrive continuously is the deciding factor.
SSE fits screens where the server needs to keep streaming state changes. Order-processing pages can show step changes the moment they happen, operational dashboards can surface alerts as soon as they are raised, and log viewers can append new entries immediately. In practice, response buffering and connection retention matter a lot. If a proxy or CDN buffers the response, events can arrive in batches instead of immediately, and the server must clean up dropped connections while using the last event ID to keep resumption aligned. If the browser also needs to send commands back to the server, SSE alone is not enough. In that case, keeping the stream for inbound updates and separating writes into ordinary HTTP requests is the natural structure.