Sse Event Source Api
title: "EventSource API in Browsers" description: "Learn how to use the EventSource API in browsers for consuming Server-Sent Events, handling connection states, and implementing advanced patterns." weight: 14 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "sse"]
The EventSource API is the browser-side interface for consuming SSE streams. It provides automatic reconnection, event dispatching, and connection state management with minimal code.
## What You'll Learn
- EventSource constructor and options
- Event handling: onmessage, onerror, onopen
- Named event listeners
- Connection state tracking
- Closing and cleanup
## Why It Matters
The EventSource API handles all the complexity of SSE consumption automatically. Understanding its API ensures you use it correctly and handle edge cases.
## Real-World Use
A live analytics dashboard creates an EventSource for each metric category. CPU metrics use named events, memory metrics use onmessage, and a connection status indicator tracks the EventSource readyState.
## Flow Chart
```mermaid
stateDiagram-v2
[*] --> CONNECTING: new EventSource()
CONNECTING --> OPEN: Connection established
CONNECTING --> CLOSED: Failed permanently
OPEN --> CLOSED: close() called
OPEN --> CONNECTING: Connection lost
Code Examples
Example 1: Complete EventSource Usage
// Create EventSource with options
const source = new EventSource('/api/events', {
withCredentials: true, // Include cookies for CORS
});
// Connection opened
source.onopen = (event) => {
console.log('SSE connection established');
updateConnectionStatus('connected');
};
// Unnamed events
source.onmessage = (event) => {
console.log('Received:', event.data);
console.log('Last event ID:', event.lastEventId);
updateDashboard(event.data);
};
// Connection errors
source.onerror = (event) => {
if (source.readyState === EventSource.CLOSED) {
console.error('SSE connection failed permanently');
updateConnectionStatus('disconnected');
showReconnectButton();
} else if (source.readyState === EventSource.CONNECTING) {
console.log('SSE reconnecting...');
updateConnectionStatus('reconnecting');
}
};
// Named events
source.addEventListener('stock-update', (event) => {
const data = JSON.parse(event.data);
updateStockPrice(data.symbol, data.price);
});
source.addEventListener('news-alert', (event) => {
showNotification(event.data);
});
source.addEventListener('system-status', (event) => {
updateSystemStatus(JSON.parse(event.data));
});
// Check connection state
function getConnectionStatus() {
switch (source.readyState) {
case 0: return 'CONNECTING';
case 1: return 'OPEN';
case 2: return 'CLOSED';
}
}
// Clean up
function cleanup() {
source.close();
console.log('SSE connection closed');
}
Expected output: EventSource connects, dispatches events to appropriate handlers, tracks connection state, and handles errors gracefully.
Example 2: Multiple EventSources
class SSEManager {
constructor() {
this.sources = new Map();
this.listeners = new Map();
}
connect(name, url, options = {}) {
// Close existing connection
if (this.sources.has(name)) {
this.disconnect(name);
}
const source = new EventSource(url, options);
this.sources.set(name, source);
source.onopen = () => {
console.log(`SSE '${name}' connected`);
this.emit('connection', { name, status: 'connected' });
};
source.onerror = (event) => {
if (source.readyState === EventSource.CLOSED) {
console.error(`SSE '${name}' failed`);
this.emit('connection', { name, status: 'failed' });
}
};
source.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.emit('message', { name, data });
} catch (e) {
console.warn('Parse error for', name, event.data);
}
};
return source;
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event).add(callback);
return () => this.listeners.get(event)?.delete(callback);
}
emit(event, data) {
this.listeners.get(event)?.forEach(cb => cb(data));
}
disconnect(name) {
const source = this.sources.get(name);
if (source) {
source.close();
this.sources.delete(name);
this.emit('connection', { name, status: 'disconnected' });
}
}
disconnectAll() {
this.sources.forEach((source, name) => this.disconnect(name));
}
}
// Usage
const sseManager = new SSEManager();
sseManager.connect('stocks', '/api/stocks');
sseManager.connect('news', '/api/news', { withCredentials: true });
sseManager.on('message', ({ name, data }) => {
console.log(`[${name}]`, data);
});
sseManager.on('connection', ({ name, status }) => {
updateIndicator(name, status);
});
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
sseManager.disconnectAll();
});
Expected output: Manager handles multiple concurrent EventSource connections with centralized event dispatching and lifecycle management.
Example 3: EventSource Polyfill for Older Browsers
// Check for EventSource support
if (typeof EventSource === 'undefined') {
console.warn('EventSource not supported, loading polyfill');
// Dynamic import of polyfill
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/event-source-polyfill@1.0.31/src/eventsource.min.js';
document.head.appendChild(script);
script.onload = () => {
console.log('EventSource polyfill loaded');
initApp();
};
} else {
initApp();
}
function initApp() {
const source = new EventSource('/api/events');
source.onmessage = (event) => {
// Application code
};
}
// Or use a custom fetch-based fallback
function createFallbackSSE(url) {
let reader = null;
let buffer = '';
async function connect() {
try {
const response = await fetch(url, {
headers: { 'Accept': 'text/event-stream' },
});
reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop() || '';
events.forEach(eventStr => {
if (eventStr.trim()) {
const event = parseSSEEvent(eventStr);
if (event) {
dispatchEvent(event);
}
}
});
}
} catch (error) {
console.error('SSE fallback error:', error);
setTimeout(connect, 3000);
}
}
connect();
}
Expected output: Polyfill or fallback implementation enables SSE support in older browsers that do not support EventSource natively.
Common Mistakes
| Mistake | Explanation |
|---|---|
| Forgetting to handle onerror | Without error handling, silent failures leave the UI showing stale data |
| Not closing EventSource on page unload | Open connections prevent page from unloading quickly and waste resources |
| Using innerHTML with SSE data | Always sanitize or use textContent to prevent XSS from server data |
| Ignoring CORS withCredentials | Cross-origin SSE requires both server headers and withCredentials option |
| Creating too many EventSource connections | Each EventSource uses a separate HTTP connection; reuse or multiplex when possible |
Practice Questions
- How do you create an EventSource with credentials?
- What are the three readyState values of EventSource?
- How do you listen for named events in EventSource?
- What happens when EventSource encounters a network error?
- How do you cleanly close an EventSource connection?
Challenge
Build a React component that uses EventSource to receive live updates. Include connection status indicator, automatic reconnection with backoff, named event handling, and proper cleanup on component unmount.
FAQ
Mini Project
Build an SSE client library that wraps EventSource with features: automatic reconnection with exponential backoff, event filtering, connection status events, and a simple API for adding/removing event listeners. Include TypeScript type definitions.
What's Next
Learn how to implement SSE with Express.js
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro