Server-Sent Events in JavaScript Using EventSource
The EventSource interface is a built-in browser API that
enables web applications to receive real-time, unidirectional data
streams from a server over a standard HTTP connection using Server-Sent
Events (SSE). Unlike WebSockets, which are bidirectional,
EventSource is designed specifically for scenarios where
the client only needs to listen for continuous updates from the server,
such as live score updates, stock tickers, or notifications. This
article explains how EventSource works, how to initialize
and handle SSE connections in JavaScript, and how to manage stream
events effectively.
What is Server-Sent Events (SSE)?
Server-Sent Events allow a server to push data to a web client
asynchronously once an initial connection is established. It runs over
standard HTTP or HTTPS, utilizing the text/event-stream
MIME type. Because it operates over standard HTTP, SSE works natively
with HTTP/2 and existing network infrastructure, including firewalls and
load balancers, without requiring specialized protocols.
Initializing the EventSource Connection
To initiate an SSE connection in JavaScript, instantiate the
EventSource object by passing the target server endpoint
URL:
const eventSource = new EventSource('/api/stream-endpoint');If the connection requires credentials such as cookies or authorization headers in cross-origin requests, you can provide an options object:
const eventSource = new EventSource('https://example.com/api/stream', {
withCredentials: true
});Once instantiated, the browser opens a persistent HTTP connection to the server and begins listening for incoming data.
Listening for Stream Events
The EventSource interface provides built-in event
handlers to manage connection states and receive incoming messages.
1. The onopen Event
Fired when the connection to the server is successfully established.
eventSource.onopen = (event) => {
console.log('SSE connection successfully opened.');
};2. The onmessage Event
Fired when the server sends a generic, unnamed message (messages
without a custom event field).
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('New message received:', data);
};3. Custom Named Events
Servers can send custom event types. To handle these, use standard
addEventListener calls matching the event name defined by
the server.
eventSource.addEventListener('priceUpdate', (event) => {
const priceData = JSON.parse(event.data);
console.log('Custom event (priceUpdate):', priceData);
});4. The onerror Event
Fired when a network error occurs or the connection is lost.
eventSource.onerror = (error) => {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('Connection was closed.');
} else {
console.error('An error occurred with the stream:', error);
}
};Server-Side Data Format
For EventSource to interpret messages correctly, the
server must format its HTTP response body as plain text conforming to
the SSE specification:
data:Specifies the message payload.event:(Optional) Defines a custom event name.id:(Optional) Sets a unique message ID for tracking.retry:(Optional) Defines the reconnection wait time in milliseconds.\n\n: Two consecutive newline characters signify the end of a single event block.
Example server response payload:
event: priceUpdate
id: 101
data: {"symbol": "AAPL", "price": 175.50}
Connection Management and Auto-Reconnection
EventSource provides automatic reconnection out of the
box:
- Automatic Reconnects: If the connection drops
unexpectedly, the browser automatically attempts to reconnect after a
short delay (default is usually a few seconds, or as specified by the
server’s
retryfield). - Last-Event-ID: When reconnecting, the browser
automatically sends a
Last-Event-IDHTTP header containing the last received message ID, allowing the server to resume streaming from the point of disconnection. - Closing the Connection: To terminate the connection
permanently from the client side, call the
.close()method:
// Terminate the connection
eventSource.close();
console.log('Connection permanently closed.');