What are server-sent events?
The short answer
Server-sent events (SSE) let a server push updates to the browser over a single, long-lived HTTP connection. The client opens the connection once, and the server can keep sending data whenever it wants — no polling, no repeated requests. It's one-directional: the server talks, the client listens.
If you've ever seen a live news feed, a stock ticker, or a notification stream that updates without you refreshing the page, there's a good chance SSE is behind it.
How it works under the hood
The flow is surprisingly simple compared to other real-time technologies:
- The client creates a new
EventSourcepointing at a URL on your server. - The server responds with a
Content-Type: text/event-streamheader and keeps the connection open. - Whenever the server has something new to say, it writes a chunk of text to the response in a specific format.
- The browser parses each chunk and fires a
messageevent on theEventSourceobject.
On the client side, it looks like this:
const source = new EventSource('/api/updates');
source.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('New update:', data);
});
source.addEventListener('error', (event) => {
console.log(
'Connection lost, browser will retry automatically'
);
});On the server side (using Node.js as an example), you set the right headers and write to the response:
app.get('/api/updates', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const intervalId = setInterval(() => {
const data = JSON.stringify({
time: new Date().toISOString(),
});
res.write(`data: ${data}\n\n`);
}, 3000);
req.on('close', () => {
clearInterval(intervalId);
});
});Each message from the server follows a simple text format: lines starting with data:, separated by a blank line. You can also include event: to name your events, id: to set a last-event ID, and retry: to tell the browser how long to wait before reconnecting.
event: notification
data: {"text": "You have a new message"}
id: 42Automatic reconnection
Here's where things get interesting. If the connection drops — the user's wifi blips, the server restarts, whatever — the browser automatically tries to reconnect. You don't have to write any reconnection logic yourself.
Even better, if the server was sending id: fields with each message, the browser includes a Last-Event-ID header when it reconnects. The server can use that to pick up where it left off and send only the messages the client missed. This is built into the protocol for free.
The default reconnection delay is usually around 3 seconds, but the server can control it by sending a retry: field with a value in milliseconds.
Named events
By default, everything comes through as a message event. But you can use the event: field to create different channels over the same connection:
const source = new EventSource('/api/feed');
source.addEventListener('notification', (event) => {
console.log('Notification:', event.data);
});
source.addEventListener('status-update', (event) => {
console.log('Status:', event.data);
});The server sends these by including the event: line before the data: line. This lets you multiplex different types of updates over a single connection without the client having to parse and route them manually.
SSE vs WebSockets vs polling
This comparison comes up a lot, so let's be clear about the trade-offs.
Polling means the client sends a request every few seconds asking "anything new?" It's the simplest approach, but wasteful — most requests come back empty, and there's always a delay between when data is available and when the client finds out.
Server-sent events keep a connection open and let the server push data as it becomes available. It's efficient for one-way flows, runs over plain HTTP (so it works with existing infrastructure like load balancers and proxies), and handles reconnection automatically.
WebSockets open a full two-way communication channel. Both client and server can send messages at any time. They use a different protocol (ws:// or wss://), which means they need special handling at the infrastructure level.
The decision comes down to what you need:
- If you just need the server to push updates to the client, SSE is simpler and often the better choice.
- If the client also needs to send frequent messages back to the server (like a chat app or multiplayer game), you need WebSockets.
- If your data changes infrequently and latency doesn't matter, polling is fine.
Limitations worth knowing
SSE has a few constraints:
- One-directional — the server sends, the client receives. If the client needs to send data back, it uses regular HTTP requests.
- Text only — SSE transmits UTF-8 text. If you need to send binary data like images or audio, WebSockets or a different approach is more appropriate.
- Connection limit — browsers typically allow around 6 concurrent HTTP connections per domain. Since SSE uses one of those, it can eat into your budget for other requests. Using HTTP/2 largely solves this, since it multiplexes over a single TCP connection.
- No IE support — not that this matters much anymore, but SSE was never supported in Internet Explorer. Every modern browser handles it fine.
When to use SSE in practice
SSE shines when the communication pattern is "server has updates, client wants them":
- Live feeds (news, social media timelines)
- Real-time notifications
- Stock price or cryptocurrency tickers
- Progress updates for long-running server tasks
- Live log streaming
- Dashboard metrics that update in real time
For all of these, the simplicity of SSE — no special protocol, automatic reconnection, works with standard HTTP infrastructure — makes it a pragmatic choice over WebSockets.
Why interviewers ask this
This question tests whether you understand the spectrum of real-time communication options available in the browser. Many developers jump straight to WebSockets for anything real-time, without considering that SSE is simpler, more reliable, and sufficient for most use cases. Interviewers want to see that you can evaluate trade-offs and pick the right tool for the job, not just the most powerful one.