Share a single SSE connection across all browser tabs using BroadcastChannel leader election.
Browsers limit HTTP/1.1 connections to 6–8 per domain. Each tab opening its own EventSource exhausts this pool — 10 tabs means 10 SSE connections, blocking all other requests.
sse-coordinator elects one tab as the leader. Only the leader holds an EventSource connection. All other tabs receive events via the BroadcastChannel API — zero extra connections.
Tab 1 (LEADER) ──── EventSource ────▶ Your Server
Tab 2 (follower) ◀──┐
Tab 3 (follower) ◀──┤── BroadcastChannel
Tab 4 (follower) ◀──┘
When the leader tab closes, a follower automatically promotes itself and opens a new connection.
npm install sse-coordinator
# or
bun add sse-coordinator
# or
pnpm add sse-coordinatorimport { SSECoordinator } from 'sse-coordinator';
const coordinator = new SSECoordinator();
coordinator.connect({
url: 'https://api.example.com/events/stream',
eventTypes: ['notification.created', 'job.completed'],
onEvent(event) {
console.log(event.type, event.data);
},
onConnectionChange(connected) {
console.log('connected:', connected);
},
onError(error) {
console.error('SSE error:', error);
},
});
// Later:
coordinator.disconnect();Creates a coordinator instance. Each tab should create its own instance.
Starts the coordinator. The tab will either become leader (opening an EventSource) or follower (listening via BroadcastChannel).
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string |
✓ | — | SSE endpoint URL |
eventTypes |
string[] |
✓ | — | Named event types to listen for |
onEvent |
(event: SSEEvent) => void |
✓ | — | Called for every SSE event |
channelName |
string |
'sse-coordinator' |
BroadcastChannel name — must match across tabs | |
withCredentials |
boolean |
false |
Pass cookies with the EventSource request | |
maxReconnectAttempts |
number |
10 |
Max reconnection attempts before calling onError |
|
logger |
Logger |
none | Optional logger ({ debug, info, warn, error }) |
|
onError |
(error: Error) => void |
— | Called when max reconnections are exceeded | |
onConnectionChange |
(connected: boolean) => void |
— | Called when the leader's connection opens or closes |
Closes the connection and BroadcastChannel. If this tab is the leader, broadcasts a disconnect message so a follower can take over.
Returns true if this tab currently holds the EventSource connection.
interface SSEEvent {
type: string;
data: unknown;
id: string;
timestamp: string; // ISO 8601
}- Leader election — on
connect(), the tab waits 200ms for a heartbeat from an existing leader. If none arrives, it promotes itself. - Heartbeat — the leader broadcasts a heartbeat every 5 seconds. Followers monitor this.
- Failover — if no heartbeat is received for 15 seconds, a follower promotes itself. When a leader tab closes cleanly, it sends a
leader-disconnectmessage to trigger immediate promotion. - Reconnection — the leader reconnects with exponential backoff (capped at 30 seconds) up to
maxReconnectAttempts.
If you run multiple independent apps on the same domain, give each a unique channelName:
coordinator.connect({
url: '...',
eventTypes: [...],
channelName: 'my-app-sse',
onEvent: () => {},
});Requires BroadcastChannel API and crypto.randomUUID() — supported in Chrome 92+, Firefox 95+, Safari 15.4+, Edge 92+.
MIT
Made by Pareo