Build a Streaming AI Chat Agent with tRPC v11 + Next.js 15 (2026)
By Sagar Shankaran, Founder of CallSphere
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.
Key takeaways
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 $49/$99/$149 pricing with a 7-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

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.