Build a Streaming AI Chat Agent with tRPC v11 + Next.js 15 (2026)
Use tRPC v11 async generators to stream OpenAI tokens over SSE through a fully type-safe stack. Real Next.js App Router code, no WebSockets needed.
TL;DR — tRPC v11 added
async function*procedures that stream over Server-Sent Events. Wrap an OpenAIstream: truecall and you get end-to-end type-safe token streaming with zero WebSocket plumbing.
What you'll build
A Next.js 15 App Router chat UI with a tRPC chat.stream mutation that yields OpenAI tokens one at a time. The client uses the new useSubscription hook so React renders deltas as they arrive — no manual fetch + reader code.
Prerequisites
- Next.js 15.x + App Router, TypeScript 5.5+.
@trpc/server@^11,@trpc/client@^11,@trpc/react-query@^11,zod@^3.23.openai@^4.70and an API key.
Architecture
flowchart LR
UI[React useSubscription] -- SSE --> RT[tRPC HTTP route]
RT -- async generator --> OA[OpenAI stream: true]
OA -- delta --> RT --> UI
Step 1 — tRPC router with async generator
```ts import { z } from "zod"; import OpenAI from "openai"; import { router, publicProcedure } from "./trpc";
const oa = new OpenAI();
export const chatRouter = router({ stream: publicProcedure .input(z.object({ messages: z.array(z.object({ role: z.enum(["user","assistant","system"]), content: z.string(), })) })) .subscription(async function* ({ input, signal }) { const s = await oa.chat.completions.create({ model: "gpt-4o-mini", messages: input.messages, stream: true, }, { signal }); for await (const chunk of s) { const delta = chunk.choices[0]?.delta?.content; if (delta) yield { type: "delta", text: delta }; } yield { type: "done" }; }), }); ```
Step 2 — Mount the route handler
```ts // app/api/trpc/[trpc]/route.ts import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { appRouter } from "@/server/router";
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
const handler = (req: Request) => fetchRequestHandler({ endpoint: "/api/trpc", req, router: appRouter, createContext: () => ({}) });
export { handler as GET, handler as POST }; ```
Step 3 — Client provider with SSE link
```ts import { createTRPCReact, httpSubscriptionLink, splitLink, httpBatchLink } from "@trpc/react-query";
export const trpc = createTRPCReact
Step 4 — useSubscription in a component
```tsx "use client"; import { useState } from "react"; import { trpc } from "@/lib/trpc";
export function Chat() { const [text, setText] = useState(""); const [reply, setReply] = useState(""); const [run, setRun] = useState<any[]>([]);
trpc.chat.stream.useSubscription( { messages: run }, { enabled: run.length > 0, onData: (e) => e.type === "delta" && setReply((r) => r + e.text) }, );
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
return ( <form onSubmit={(e) => { e.preventDefault(); setReply(""); setRun([{ role: "user", content: text }]); }}> <input value={text} onChange={(e) => setText(e.target.value)} />
{reply}
);
}
```
Step 5 — Cancel mid-stream
When the user navigates away, React Query unmounts the subscription. tRPC v11 propagates the abort signal into the OpenAI client ({ signal }), which cancels the upstream request — saving tokens.
Pitfalls
- Edge runtime:
runtime = "edge"works for SSE but limits some Node-only OpenAI helpers — pickruntime = "nodejs"if you needstream.controller. - Buffering proxies: NGINX, Cloudflare, Vercel default proxies can buffer SSE — set
X-Accel-Buffering: noor useresponseInit: { headers: { "X-Accel-Buffering": "no" } }. - JSON-line vs SSE: tRPC v11 auto-picks SSE for browsers; if you call from a non-browser client, switch to
httpLinkand parse JSON-lines.
How CallSphere does this in production
CallSphere's Sales product (Node.js 20 + React 18 + Vite) ships a near-identical tRPC pipe for real-time SDR chat across 37 agents, 90+ tools, and 115+ DB tables — used in 6 verticals at $149/$499/$1,499 pricing with a 14-day no-card trial and 22% affiliate. p95 first-token latency is ~280ms.
FAQ
Does this work with React Server Components? The streaming part is a client component, but the trigger can be a server action that primes run.
Why not WebSockets? SSE is HTTP/1.1 friendly, survives serverless, and tRPC v11 picked it as the default for that reason.
Can I stream tool calls? Yes — yield { type: "tool_call", name, args } from the generator and render a tool UI on the client.
What about Vercel AI SDK? AI SDK and tRPC are complementary — wrap streamText inside a tRPC subscription if you want both.
Sources
- tRPC v11 - Subscriptions over SSE - https://trpc.io/docs/server/subscriptions
- tRPC examples-next-sse-chat - https://github.com/trpc/examples-next-sse-chat
- OpenAI streaming API - https://platform.openai.com/docs/api-reference/streaming
- Vercel AI SDK + tRPC discussion - https://github.com/vercel/ai/discussions/3236
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.