HTML5 Server-Sent Events (SSE)

Server-Sent Events allow servers to push real-time updates to web pages over HTTP. SSE provides one-way communication from server to client for notifications, live feeds, and real-time data.

Creating an EventSource

The EventSource API creates a persistent connection to the server.

Server sends updates as text/event-stream content type.

Connections automatically reconnect if disconnected.

SSE is simpler than WebSockets but only supports server-to-client communication.

Perfect for real-time notifications, stock tickers, news feeds, and live sports scores.

<!-- HTML page -->
<div id="notifications"></div>

<script>
if (typeof EventSource !== 'undefined') {
  const source = new EventSource('/events');
  
  source.onmessage = function(event) {
    const div = document.getElementById('notifications');
    const notification = document.createElement('p');
    notification.textContent = event.data;
    div.appendChild(notification);
  };
  
  source.onerror = function(error) {
    console.error('EventSource error:', error);
  };
} else {
  document.getElementById('notifications').textContent = 
    'Server-Sent Events not supported.';
}
</script>

<!-- Server (Node.js example) -->
<script>
app.get('/events', function(req, res) {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  
  // Send event every 2 seconds
  const intervalId = setInterval(() => {
    const data = `data: Server time: ${new Date().toLocaleTimeString()}\n\n`;
    res.write(data);
  }, 2000);
  
  // Clean up on client disconnect
  req.on('close', () => {
    clearInterval(intervalId);
  });
});
</script>

Event Types and Named Events

Server can send named events using the event: field.

Use addEventListener() to listen for specific event types.

The data: field contains the message payload.

The id: field sets an event ID for reconnection tracking.

The retry: field suggests reconnection delay in milliseconds.

<!-- Client -->
<div id="stock"></div>
<div id="news"></div>

<script>
const source = new EventSource('/stream');

// Listen for 'stock' events
source.addEventListener('stock', function(event) {
  const data = JSON.parse(event.data);
  document.getElementById('stock').textContent = 
    `${data.symbol}: $${data.price}`;
});

// Listen for 'news' events
source.addEventListener('news', function(event) {
  const div = document.getElementById('news');
  const newsItem = document.createElement('p');
  newsItem.textContent = event.data;
  div.prepend(newsItem);
});

// Handle connection open
source.onopen = function() {
  console.log('Connection established');
};

// Handle errors
source.onerror = function(error) {
  console.error('Connection error:', error);
};
</script>

<!-- Server response format -->
<script>
// Server sends:
event: stock
data: {"symbol":"AAPL","price":150.25}
id: 1

event: news  
data: Breaking: New product announced
id: 2

// Default 'message' event (no event: field)
data: Simple message

retry: 10000
</script>

Connection Management

EventSource automatically reconnects after disconnection.

The readyState property indicates connection status: CONNECTING (0), OPEN (1), CLOSED (2).

Call close() to manually close the connection.

The Last-Event-ID header is sent on reconnection to resume from last event.

Browsers typically wait 3 seconds before reconnecting; servers can override with retry: field.

<button onclick="startStream()">Start</button>
<button onclick="stopStream()">Stop</button>
<p>Status: <span id="status">Not connected</span></p>
<div id="messages"></div>

<script>
let eventSource;

function startStream() {
  if (eventSource) {
    eventSource.close();
  }
  
  eventSource = new EventSource('/notifications');
  
  eventSource.onopen = function() {
    document.getElementById('status').textContent = 'Connected';
    console.log('ReadyState:', eventSource.readyState); // 1 = OPEN
  };
  
  eventSource.onmessage = function(event) {
    const div = document.getElementById('messages');
    const msg = document.createElement('p');
    msg.textContent = `[${new Date().toLocaleTimeString()}] ${event.data}`;
    div.prepend(msg);
  };
  
  eventSource.onerror = function() {
    if (eventSource.readyState === EventSource.CLOSED) {
      document.getElementById('status').textContent = 'Connection closed';
    } else {
      document.getElementById('status').textContent = 'Reconnecting...';
    }
  };
}

function stopStream() {
  if (eventSource) {
    eventSource.close();
    document.getElementById('status').textContent = 'Disconnected';
    console.log('ReadyState:', eventSource.readyState); // 2 = CLOSED
  }
}
</script>

SSE vs WebSockets

SSE is one-way (server to client), WebSockets are bidirectional.

SSE uses HTTP, WebSockets use a separate protocol (ws://).

SSE automatically reconnects, WebSockets require manual reconnection.

SSE is simpler for server push scenarios like notifications or live updates.

WebSockets are better for chat apps, gaming, or when client needs to send frequent updates.

SSE has built-in event IDs for resuming after disconnection.

<!-- SSE: Server to client only -->
<script>
const sse = new EventSource('/live-feed');
sse.onmessage = (e) => console.log('Received:', e.data);
// Client cannot send data back through SSE
</script>

<!-- WebSocket: Bidirectional -->
<script>
const ws = new WebSocket('ws://example.com/socket');
ws.onmessage = (e) => console.log('Received:', e.data);
ws.send('Hello server'); // Can send data to server
</script>

<!-- When to use SSE -->
<script>
// Live notifications
// Stock price updates
// News feeds
// Server logs streaming
// Real-time monitoring dashboards
// Social media timelines
</script>

<!-- When to use WebSockets -->
<script>
// Chat applications
// Multiplayer games
// Collaborative editing
// Video conferencing
// Any scenario requiring client-to-server messages
</script>