Streaming

Client Transports

Consume Anvia streams from React and custom clients.

Use @anvia/react when a browser or React UI needs to consume streaming agent events.

The transport boundary is an async iterable:

type EventTransport<TRequest, TEvent> = {
  send(request: TRequest, options?: TransportOptions): AsyncIterable<TEvent>;
};

That lets UI state consume JSONL, SSE, WebSocket, local, or custom transports without changing the hook.

1. Fetch Events Directly

import { fetchEventStream } from "@anvia/react";

for await (const event of fetchEventStream<AgentStreamEvent>("/api/chat", {
  method: "POST",
  body: JSON.stringify({ message: "Hello", stream: true }),
  headers: { "content-type": "application/json" },
})) {
  if (event.type === "text_delta") {
    renderDelta(event.delta);
  }
}

fetchEventStream(...) returns AsyncIterable<TEvent> and supports format: "jsonl" or format: "sse". If format is omitted, it infers SSE from content-type: text/event-stream and otherwise reads JSONL.

2. Create a Fetch Transport

import { createFetchTransport } from "@anvia/react";

const transport = createFetchTransport({
  endpoint: "/api/chat",
  format: "jsonl",
});

for await (const event of transport.send({ message: "Hello", stream: true })) {
  console.log(event);
}

The default transport uses POST, JSON request bodies, and JSONL response parsing.

3. Use Chat State in React

import { useChat } from "@anvia/react";

export function Chat() {
  const chat = useChat({
    endpoint: "/api/chat",
  });

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        void chat.send();
      }}
    >
      <div>{chat.text}</div>
      <input value={chat.input} onChange={(event) => chat.setInput(event.target.value)} />
      <button disabled={chat.status === "streaming"}>Send</button>
    </form>
  );
}

Passing endpoint is shorthand for a default JSONL fetch transport.

4. Pass a Custom Transport

const chat = useChat({
  transport: {
    async *send(request, options) {
      yield* myWebSocketTransport(request, options);
    },
  },
});

useChat(...) only consumes AsyncIterable events. It does not depend on fetch, JSONL, or SSE.