Skip to content

Sse Event Types

DodaTech 5 min read

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

  1. How do you send a named event from the server?
  2. How do you listen for named events on the client?
  3. What happens to named events if no listener is registered?
  4. How do you remove an event listener in EventSource?
  5. 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

Can I use addEventListener for unnamed events?

No, unnamed events only trigger onmessage. Use addEventListener for events with the event: field only.

How many named event types can I have?

There is no limit, but keep the number manageable. Group related events and use payload fields for sub-types.

Do named events support bubbling?

No, EventSource does not support event bubbling. Each named event goes only to its specific listeners.

What is the performance cost of named events?

Named events have no meaningful performance overhead compared to unnamed events. They are parsed identically.

Can I dynamically add event listeners?

Yes, you can call addEventListener at any time. Listeners added after the connection starts will receive future events.

How do I list all registered event listeners?

EventSource does not provide an API to list listeners. Track them externally in your application code.

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