useEventStream

Hook that subscribes to a Server-Sent Events endpoint of this origin, e.g. the messages of a conversation as an agent writes them.

const state = useEventStream<Message>("/api/conversations/42/events", {  events: ["message", "done"],  onEvent: (message, { type }) => setMessages((m) => [...m, message]),});

Installation

Once per app, set up Aedis UI. The CLI verifies the signed release before writing anything and pins every dependency:

npx @aedis/cli@0.1.0 init

Import the theme once in your global CSS, after Tailwind:

app/globals.css
@import "tailwindcss";@import "./styles/aedis-theme.css";

Then add the component:

npx @aedis/cli@0.1.0 add use-event-stream# pnpm dlx @aedis/cli@0.1.0 add use-event-stream · yarn dlx @aedis/cli@0.1.0 add use-event-stream · bunx @aedis/cli@0.1.0 add use-event-stream

Props

PropTypeDefaultDescription
pathstring | null—Same-origin path; null pauses. A token in the query string is refused.
onEvent(data, { type, id }) => void—Called with the parsed JSON of each allowed event; malformed JSON is ignored.
eventsstring[]["message"]Allow-list of event types.
enabledbooleantruePause without changing the path.
lastEventIdstring—Resume after this id (Last-Event-ID, 256 printable characters at most).
maxEventBytes / maxConnectionBytesnumber64 KiB / 16 MiBBigger events are dropped; past the connection limit it reconnects from the last id.
heartbeatMs / maxRetries / retryMs / maxRetryMsnumber15000 / 8 / 1000 / 30000Silence for 2x heartbeat reconnects; backoff with jitter honours the server's retry:.
returns"connecting" | "open" | "retrying" | "closed" | "error"—Connection state.

Security

  • fetch with a streamed body, not EventSource: same-origin path, cookies same-origin only, redirects refused, X-Requested-With, no-store.
  • Never a token in the query string: such paths throw. The session cookie (or a BFF, see bff-stream) authenticates the stream.
  • Parser with limits per event and per connection; event types from an allow-list; malformed JSON ignored.
  • Text is decoded in stream mode and stripInvisible runs on each assembled event, so a chunk boundary inside a surrogate pair cannot smuggle Unicode tags.
  • 204 stops for good, other 4xx stop with an error state, 5xx and network errors retry with capped backoff; it aborts on unmount.