This older article and its examples have not yet been reviewed against current APIs. Check the latest documentation before using the code or treating any example as a measured result.
A streamed Markdown response can arrive halfway through a code fence or table. Until the rest arrives, readers may see raw syntax. One approach is to render complete blocks as Markdown and show the unfinished tail as plain text.
Keep the page stable
Reserve space for the response where you can. A skeleton gives the answer somewhere to appear and can reduce layout shifts while content streams in.
Keep receiving the stream, but avoid forcing React to render for every token. Group UI updates on an animation-frame budget.
Handle cancellation and progress
Give readers a way to stop. Wire cancellation to the stop button and to navigation so a stream is not left running after the reader leaves.
Show progress only when the system can report it accurately. A label such as “Searching documents” or “Preparing a response” tells the reader what is happening without inventing a stage.
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o-mini"),
system: SYSTEM_PROMPT,
messages,
experimental_throttle: 40, // ms between UI flushes
});
return result.toDataStreamResponse();
}
// client: render only complete markdown blocks
function useStableMarkdown(chunks: string) {
return useMemo(() => {
const safe = chunks.slice(0, chunks.lastIndexOf("\n\n") + 2);
return { stable: safe || chunks, tail: chunks.slice(safe.length) };
}, [chunks]);
}Show progress only when the system can report it accurately. A label such as “Searching documents” or “Preparing a response” tells the reader what is happening without inventing a stage.