Recipe

Server-Sent Events

Stream real-time updates from server to browser over a single long-lived HTTP connection.

Overview

SSE is a lightweight push protocol built on standard HTTP. The server holds the connection open and writes text/event-stream frames. The browser consumes them via the native EventSource API — no WebSocket handshake, no custom framing.

When to use it

  • One-way server→client data flow
  • Progress bars, live logs, notification feeds
  • Edge Functions that stream token-by-token
  • Anywhere WebSocket is overkill

Minimal server (Node)

res.writeHead(200, {
  'Content-Type': 'text/event-stream',
  'Cache-Control': 'no-cache',
  Connection: 'keep-alive',
})

let n = 0
const id = setInterval(() => {
  res.write(`data: ${JSON.stringify({ tick: n++ })}\n\n`)
}, 1000)

req.on('close', () => clearInterval(id))

Client (browser)

const es = new EventSource('/api/stream')
es.onmessage = (e) => console.log(JSON.parse(e.data))
es.onerror = () => es.close()

Vercel Edge caveats

Edge Functions have a maximum duration. For long-lived streams use a regional Serverless Function or a dedicated real-time backend. Always set a heartbeat comment line (: ping) every 15s to keep proxies from closing idle connections.