Sse Event Types
title: "SSE Named Event Types" description: "Learn how to use named event types in Server-Sent Events for dispatching different event categories to specific handlers on the client." weight: 18 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "sse"]
Named events allow SSE to deliver different types of data through a single connection. The client can register specific handlers for each event type, making SSE suitable for complex real-time applications.
## What You'll Learn
- The `event:` field in SSE
- Named event listener registration
- Event dispatch on the client
- Mixing named and unnamed events
- Use cases for named events
## Why It Matters
Without named events, the client must inspect every message payload to determine its type. Named events provide native dispatching, reducing code complexity and improving performance.
## Real-World Use
A live sports platform sends multiple event types through one SSE connection: score updates, player stats, game status changes, and commercial breaks. Each event type has a dedicated handler updating different parts of the UI.
## Flow Chart
```mermaid
flowchart LR
A[SSE Stream] --> B{Event Type}
B -->|score-update| C[Scoreboard Handler]
B -->|player-stat| D[Player Stats Handler]
B -->|game-status| E[Status Handler]
B -->|unnamed| F[Default Handler]
Code Examples
Example 1: Server with Multiple Named Events
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
});
// Named event: score update
setInterval(() => {
res.write(
'event: score-update\n' +
`data: ${JSON.stringify({
home: { team: 'Lions', score: Math.floor(Math.random() * 100) },
away: { team: 'Tigers', score: Math.floor(Math.random() * 100) },
})}\n` +
'\n'
);
}, 3000);
// Named event: player stat
setInterval(() => {
res.write(
'event: player-stat\n' +
`data: ${JSON.stringify({
player: `Player ${Math.floor(Math.random() * 30)}`,
points: Math.floor(Math.random() * 40),
assists: Math.floor(Math.random() * 15),
})}\n` +
'\n'
);
}, 5000);
// Named event: game status
setTimeout(() => {
res.write(
'event: game-status\n' +
'data: {"status": "halftime"}\n' +
'\n'
);
}, 15000);
setTimeout(() => {
res.write(
'event: game-status\n' +
'data: {"status": "final"}\n' +
'\n'
);
}, 30000);
// Unnamed event (default)
setInterval(() => {
res.write(
'data: {"message": "Keep-alive heartbeat"}\n' +
'\n'
);
}, 10000);
}).listen(3000);
Expected output: Server sends different event types (score-update, player-stat, game-status) through a single SSE connection.
Example 2: Client with Specific Event Handlers
const source = new EventSource('/game/events');
// Score updates
source.addEventListener('score-update', (event) => {
const data = JSON.parse(event.data);
document.getElementById('home-score').textContent = data.home.score;
document.getElementById('away-score').textContent = data.away.score;
document.getElementById('home-team').textContent = data.home.team;
document.getElementById('away-team').textContent = data.away.team;
});
// Player statistics
source.addEventListener('player-stat', (event) => {
const data = JSON.parse(event.data);
const row = document.createElement('tr');
row.innerHTML = `
<td>${data.player}</td>
<td>${data.points}</td>
<td>${data.assists}</td>
`;
document.getElementById('player-stats').appendChild(row);
});
// Game status changes
source.addEventListener('game-status', (event) => {
const data = JSON.parse(event.data);
const statusEl = document.getElementById('game-status');
switch (data.status) {
case 'halftime':
statusEl.textContent = 'Halftime';
statusEl.className = 'status-halftime';
break;
case 'final':
statusEl.textContent = 'Game Over';
statusEl.className = 'status-final';
source.close(); // No more events needed
break;
default:
statusEl.textContent = data.status;
}
});
// Unnamed events fallback
source.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.message === 'Keep-alive heartbeat') {
updateConnectionIndicator(true);
}
};
// Error handling
source.addEventListener('error', () => {
updateConnectionIndicator(false);
});
Expected output: Each named event updates a specific part of the UI, unnamed events handle keep-alive, and the connection closes on game final.
Example 3: Dynamic Event Listener Management
class SSEManager {
constructor(url) {
this.source = new EventSource(url);
this.listeners = new Map();
this.defaultListeners = new Set();
this.source.onmessage = (event) => {
this.defaultListeners.forEach(cb => cb(event));
};
}
on(eventType, callback) {
if (eventType === 'message') {
this.defaultListeners.add(callback);
return () => this.defaultListeners.delete(callback);
}
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Set());
this.source.addEventListener(eventType, (event) => {
this.listeners.get(eventType)?.forEach(cb => cb(event));
});
}
this.listeners.get(eventType).add(callback);
return () => {
this.listeners.get(eventType)?.delete(callback);
if (this.listeners.get(eventType)?.size === 0) {
this.listeners.delete(eventType);
}
};
}
once(eventType, callback) {
const wrapper = (event) => {
callback(event);
this.off(eventType, wrapper);
};
return this.on(eventType, wrapper);
}
off(eventType, callback) {
this.listeners.get(eventType)?.delete(callback);
}
close() {
this.source.close();
this.listeners.clear();
this.defaultListeners.clear();
}
}
// Usage
const sse = new SSEManager('/api/events');
const unsubScore = sse.on('score-update', (event) => {
updateScoreboard(JSON.parse(event.data));
});
const unsubStatus = sse.on('game-status', (event) => {
handleGameStatus(JSON.parse(event.data));
});
sse.once('game-start', (event) => {
console.log('Game started!', event.data);
});
// Unsubscribe when no longer needed
unsubScore();
Expected output: Manager supports adding/removing named event listeners, one-time listeners, and proper cleanup.
Common Mistakes
| Mistake | Explanation |
|---|---|
| Using onmessage for all events | Named events do not trigger onmessage; they dispatch to addEventListener handlers |
| Misspelling event names | Event names must match exactly between server and client (case-sensitive) |
| Not removing listeners | Event listeners accumulate if not cleaned up when components unmount |
| Sending too many event types | Too many event types complicate client code; group related events into categories |
| Forgetting event: field | Without the event: field, the event is unnamed and goes to onmessage only |
Practice Questions
- How do you send a named event from the server?
- How do you listen for named events on the client?
- What happens to named events if no listener is registered?
- How do you remove an event listener in EventSource?
- Can you have both named and unnamed events in the same stream?
Challenge
Build an SSE-powered dashboard that handles 10+ named event types for different metrics (CPU, memory, disk, network, uptime, processes, alerts, etc.). Each event type updates a specific widget, and the dashboard supports dynamic subscription to event types.
FAQ
Mini Project
Build a multi-source notification center using SSE with named events. Different event types (email-received, mention, task-assigned, build-complete, deployment-status) each update specific sections of a notification panel.
What's Next
Learn about SSE auto-reconnection
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro