Streaming chat UI with Next.js.
Token-by-token streaming over SSE, cancellation, mid-stream error handling, and the API key stays server-side. ~150 lines of TypeScript.
Architecture — Three pieces
- Next.js route handler on the server — proxies the browser request to Anoman so your API key never reaches the client.
- useChat hook — manages message state, parses SSE, exposes
send()+cancel(). - ChatBox component — renders messages + input. Disables input while streaming. Shows a Stop button.
1. Server-side proxy — Pipe Anoman's stream through to the browser
The route handler calls Anoman, then re-emits each chunk as SSE for the browser. We don't modify chunks — pass them through verbatim so the _anoman metadata frame reaches the client unchanged.
app/api/chat/route.ts — Next.js streaming proxy
2. SSE parser hook — Append deltas to the last message
Persistent buffer for chunk-boundary tolerance. Mid-stream error frame is thrown so the catch block surfaces it. AbortController for cancellation.
hooks/useChat.ts — React state + SSE parser
3. UI component — Stop button while streaming, Send when idle
ChatBox.tsx — minimal React component
Production tips
- Cloudflare proxy compatibility — Anoman's edge forwards SSE frames as they arrive. Each chunk resets the proxy timeout, so streaming responses survive arbitrarily long completions.
- Show guardrail status inline — the final
_anomanframe has the full guardrail breakdown. Render a small "Verified clean ✓" badge under each assistant message. - Rate limit handling — when your route handler hits 429 from Anoman, surface a friendly "Slow down" message to the user. The header
retry-aftertells you the wait. - Multi-turn context — pass the full
messagesarray on every request. Anoman + the upstream handle the rest. - Persistent sessions — add
anoman-session-idheader to group all turns under one trace in the dashboard Sessions view.
See the playground for a full reference UI.
3-pane streaming compare with model picker + parameter controls.