Skip to content

EventSource API

The goal of these helpers is to be as close as possible to the client EventSource, however it needs to diverge since the javascript API is receive only, but the server needs to send data.

When it's being created you must give it the request object, and you can optionally defined how often keep-alive pulsed should be sent (defaults to 30sec).

import { EventSource } from "htmx-router/event-source";

export function loader({ request }: RouteContext) {
  const source = new EventSource(request);

  const timer = setInterval(() => {
    source.dispatch("message", "hello, world every 10sec");
    if (source.readyState === 2) { // the source has been closed
      clearInterval(timer);
    }
  }, 1000);

  return source.response();
}

An EventSource will also auto close when the server receives a SIGTERM or SIGTERM so it can gracefully shutdown.

There is also a EventSourceSet which is designed for pooling streams to send the same live information to multiple connections. EventSources will be auto removed from the set if it is closed when a .dispatch is issued.

import { EventSource, EventSourceSet } from "htmx-router/event-source";

const group = new EventSourceSet();
setInterval(() => {
  group.dispatch("message", "hello everyone");
}, 1000);

export function loader({ request }: RouteContext) {
  const source = new EventSource(request);
  group.add(source);

  return source.response();
}

Note using group.delete() or group.clear() will not close the connection, instead just removing them from the group. This means you will need to manually close the source when you delete it from the set, and use group.closeAll().

Tips

Combining this with htmx sse can allow you to stream live data to multiple clients at once, or create your own pool of sources which manages which user has a lock on something, and on an interval you send a lock icon down for if they do or do not have the lock. Then when the dispatch fails you unlock the entity from that user since the connection was closed. Then you don't have to rely on your client successfully sending a beacon when the window is closed to free the lock.