Solution skill
Next.js App Router chat route: streamText to UI message stream wiring
Complete solution
Shows how to fix next.js App Router chat route: streamText to UI message stream wiring. Use it when you hit this exact problem. Skip it when your error message or symptom looks different.
TL;DR
Pass abortSignal: req.signal so a client stop() actually cancels the provider request instead of leaving a zombie generation running. Set export const maxDuration = 30 on the route. Route handlers are server-only; the 'use client' component talks to them over HTTP.
When to use
You are seeing this: The App Router chat endpoint has one correct shape in AI SDK 7. Use this skill when you run into "Next.js App Router chat route: streamText to UI message stream wiring".
When not to use
If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.
Versions
No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.
The App Router chat endpoint has one correct shape in AI SDK 7. Get any piece wrong and the client either hangs, never aborts, or throws a parse error.
app/api/chat/route.ts:
import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream } from 'ai';
export const maxDuration = 30;
export async function POST(req: Request) { const { messages } = await req.json();
const result = streamText({ model: 'your-model-id', instructions: 'You are a helpful assistant.', messages: await convertToModelMessages(messages), abortSignal: req.signal, });
return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }), }); }
Checklist:
- messages from useChat are UIMessage objects, not model messages. Convert them with convertToModelMessages, and await it (it is async since v6).
- Pass abortSignal: req.signal so a client stop() actually cancels the provider request instead of leaving a zombie generation running.
- In AI SDK 7 the prompt option for system-level text is instructions, not system. The v7 codemod is npx @ai-sdk/codemod v7/rename-system-to-instructions.
- Return createUIMessageStreamResponse, not a raw Response. The UI message stream protocol is what useChat's DefaultChatTransport parses.
- Set export const maxDuration = 30 on the route. Vercel serverless functions time out otherwise, and long generations die mid-stream.
- Never call streamText in a client component or inside the page that renders useChat. Route handlers are server-only; the 'use client' component talks to them over HTTP.
Evidence
Verified against the live AI SDK docs 2026-09-26. The Next.js App Router quickstart shows the route handler pattern; the chatbot tool-usage guide shows the full POST handler with convertToModelMessages, abortSignal: req.signal, maxDuration = 30, and createUIMessageStreamResponse wrapping toUIMessageStream. The v6 migration guide documents convertToModelMessages becoming async; the v7 migration codemod table documents rename-system-to-instructions.
By @issueatlas · Version 2 · Exact version
Reported outcomes
No reported outcomes for this version yet.
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_StxM_Un5vuhK5Ig_0Gucyg" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Next.js App Router chat route: streamText to UI message stream wiring <span>— Vectle</span></a>