Server Sent Events
MediumWhat is SSE?
- One way. Data flows server to browser only; the client cannot send on this channel.
- One connection. One normal HTTP request, kept open. The server writes many messages down it.
- Built in. The browser's
EventSourceopens the stream and parses messages for you.
Step through it
Click through the steps to watch one HTTP connection open, stay open, and stream live prices. Push your own updates or drop the connection to see SSE reconnect on its own.
SYSTEM DESIGN · FRONTENDServer-Sent Events
idle
Overviewopen request→client to server
←event streamserver to client
A live stream over one HTTP connectionServer-Sent Events (SSE) let a server push a continuous stream of updates to the browser — over a single, ordinary HTTP connection that it holds open. Step through to watch a live price dashboard stay in sync.
How the connection works
Ordinary HTTP, used in a clever way:
- The browser calls
new EventSource('/prices'), a normal HTTP GET. - The server replies with
Content-Type: text/event-streamand does not close the response. - It holds the connection open.
- On any update it writes a small text message; the browser receives it right away.
The shape of an event
Each message is plain text lines (field: value), ended by a blank line:
id: 42event: pricedata: {"symbol":"AAPL","price":189.51}retry: 3000↵ (blank line ends the event)data:the payload, the only required field (often JSON).event:an optional name; without it the type is"message".id:a cursor the browser remembers and replays on reconnect.retry:reconnect delay in milliseconds.
Client
const source = new EventSource('/prices');// Plain messages (no event name)source.onmessage = (event) => updateUI(JSON.parse(event.data));// A named eventsource.addEventListener('price', (event) => updateUI(JSON.parse(event.data)));// The browser retries on its ownsource.onerror = () => console.log('Lost. Retrying...');source.close(); // stop when doneServer
app.get('/prices', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); let id = 0; const timer = setInterval(() => { id++; res.write(`id: ${id}\n`); res.write(`event: price\n`); res.write(`data: ${JSON.stringify(getPrice())}\n\n`); // blank line ends it }, 1000); req.on('close', () => clearInterval(timer)); // clean up on disconnect});Reconnecting
Connections drop (wifi, server restarts). SSE handles it for you:
- The browser reconnects on its own after a short wait.
- It sends a
Last-Event-IDheader, the id of the last message it received. - The server resumes from there, so nothing is lost. You write no retry code.
When to use it
Great for
- Live dashboards and metrics
- Notifications and alerts
- Activity and news feeds
- Progress of a long job
- Streaming AI text, token by token
Think twice if
- You need two-way talk (chat, games): use a WebSocket.
- You need to send binary data: SSE is text only.
One limit to remember
Over HTTP/1.1 a browser allows only about 6 open connections per domain. HTTP/2 removes this limit, so prefer it in production.
Quick quiz
Four questions. Tap an answer to check it.
In SSE, which way does data flow?
Which header marks the response as an SSE stream?
What marks the end of one SSE message?
If the connection drops, what does the browser do?