HTML APIs
What are Server-Sent Events in HTML5?
Server-Sent Events (SSE) let a server push a continuous stream of updates to the browser over a single long-lived HTTP connection, received through the simple, built-in EventSource API.
What are Server-Sent Events and when to use them?
Server-Sent Events are an HTML5 standard for one-way, real-time communication from the server to the browser. The client opens a connection once and the server keeps it open, streaming text messages whenever it has new data - live scores, stock tickers, notifications, or a progress feed. The browser side is handled entirely by the built-in EventSource interface.
Unlike normal request/response, where the client must keep asking, SSE lets the server push data as it becomes available. It runs over ordinary HTTP, so it passes through most firewalls and proxies without special setup, and the browser reconnects automatically if the connection drops.
Syntax
On the client you create an EventSource pointing at a server URL that responds with the text/event-stream content type. Its onmessage handler fires for each unnamed message, onopen when the connection is established, and onerror on failure. addEventListener lets you listen for custom named events.
const source = new EventSource("/events"); // must respond with text/event-stream
source.onopen = () => console.log("Connection opened");
// fires for unnamed ("message") events
source.onmessage = (event) => {
console.log("New data:", event.data);
document.getElementById("feed").textContent = event.data;
};
// fires for custom named events (server sends "event: price")
source.addEventListener("price", (event) => {
console.log("Price update:", event.data);
});
source.onerror = (err) => {
console.warn("SSE error - the browser will retry automatically", err);
};
// close the stream when you no longer need it
// source.close();Example: the client side of a live feed
This complete page connects to an SSE endpoint and shows each message as it arrives. It needs a server that streams text/event-stream (see the next section) - the EventSource part is all the browser needs.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE demo</title>
<style>
body { font-family: system-ui, sans-serif; padding: 24px; }
#feed { margin-top: 16px; padding: 12px; background: #f1f5f9;
border-radius: 8px; min-height: 24px; }
#status { color: #64748b; font-size: 13px; }
</style>
</head>
<body>
<h3>Live server feed</h3>
<div id="status">Connecting...</div>
<div id="feed">Waiting for messages...</div>
<script>
const feed = document.getElementById("feed");
const status = document.getElementById("status");
if ("EventSource" in window) {
const source = new EventSource("/events");
source.onopen = () => status.textContent = "Connected";
source.onmessage = (e) => feed.textContent = e.data;
source.onerror = () => status.textContent = "Disconnected - retrying...";
} else {
status.textContent = "EventSource is not supported in this browser.";
}
</script>
</body>
</html>The server side
The endpoint must respond with the Content-Type text/event-stream and keep the connection open. Each message is plain text: lines beginning with data:, optionally event: for a named event, id: for a message ID, and retry: to set the reconnect delay. A blank line ends a message and flushes it to the browser.
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
let count = 0;
const timer = setInterval(() => {
res.write(`id: ${count}\n`);
res.write(`data: Server time is ${new Date().toISOString()}\n\n`);
count++;
}, 1000);
req.on("close", () => clearInterval(timer)); // stop when the client leaves
});SSE vs WebSockets
SSE is one-way (server to client) and text-only, whereas WebSockets are full-duplex (both directions) and support binary data. If you only need to push updates downstream, SSE is simpler and reconnects for free; if the client must also stream data back in real time, use WebSockets.
| Aspect | Server-Sent Events | WebSockets |
|---|---|---|
| Direction | One-way: server to client | Two-way (full-duplex) |
| Protocol | Plain HTTP (text/event-stream) | ws:// or wss:// upgrade |
| Data type | UTF-8 text only | Text and binary |
| Auto-reconnect | Built in | Must be coded manually |
| Best for | Feeds, notifications, live tickers | Chat, games, collaborative editing |
EventSource is supported in all modern browsers (Chrome, Firefox, Safari, Edge). Over HTTP/1.1 browsers limit open connections per origin (about 6), which caps concurrent EventSource streams; HTTP/2 removes this limit.
The browser sends the last received event ID back in the Last-Event-ID request header when it reconnects, so include an id: field on each message to let your server resume the stream without gaps.
Key Takeaways
- SSE streams updates one-way from server to browser over a single HTTP connection.
- The client is just new EventSource(url) with onmessage, onopen and onerror handlers.
- The server responds with Content-Type text/event-stream and writes data: lines ending in a blank line.
- The browser reconnects automatically and resumes using the Last-Event-ID header.
- Choose SSE for downstream feeds; choose WebSockets when the client must send data back too.
