Streaming LLM Responses
Learn how to stream LLM responses to the browser, including request architecture, streaming protocols, frontend handling, cancellation, errors, buffering, security, and performance considerations.
Streaming LLM responses allows an application to display generated content as it becomes available instead of waiting for the model to finish the entire response. This is one of the most common techniques used to make AI chat interfaces feel faster and more responsive.
Without streaming, the application usually waits for the complete model response and then displays it. With streaming, the response is delivered progressively, allowing the user to see the beginning of the answer while the model is still generating the rest.
Streaming does not necessarily make the model generate tokens faster. Its main benefit is reducing the time the user waits before seeing useful output.
What Is LLM Response Streaming?
In a traditional request-response flow, the client sends a request and receives one completed response. An LLM may spend several seconds generating a long answer before the response is returned.
Non-streaming:
Request ───────────────────────→ AI
↓
Generate entire response
↓
Response ←───────────────────────┘With streaming, the server starts sending pieces of the generated response as they arrive.
Streaming:
Request ───────────────────────→ AI
↓
Response chunk 1 ←───────────────┤
Response chunk 2 ←───────────────┤
Response chunk 3 ←───────────────┤
Response chunk 4 ←───────────────┤
... │
Final chunk ←────────────────────┘Why Streaming Matters for AI Applications
Long AI responses can create a large gap between submitting a request and receiving visible content. Even if the total generation time remains unchanged, progressive output makes the application feel significantly more responsive.
| Metric | Non-Streaming | Streaming |
|---|---|---|
| Time to first visible text | Usually near the end | Usually much earlier |
| Perceived responsiveness | Lower | Higher |
| Progressive rendering | No | Yes |
| Implementation complexity | Lower | Higher |
| Cancellation handling | Simpler | Requires additional handling |
Streaming is especially useful for chatbots, coding assistants, writing tools, summarization applications, and other interfaces where generated responses may contain many tokens.
Time to First Token vs Total Response Time
Two latency measurements are particularly useful when evaluating streaming: time to first token and total response time. Time to first token measures how long the user waits before the first generated content becomes available. Total response time measures how long it takes to receive the complete response.
Request
↓
Waiting
↓ First token
Generation
↓
Complete responseStreaming primarily improves the first metric. The model still needs to generate the complete response, so the total amount of generation work does not disappear.
How Streaming Works in a Web Application
A typical AI web application streams data through the backend. The browser sends a request to the application's server, the server sends a streaming request to the AI provider, and generated chunks are forwarded to the browser.
Browser
|
| POST /api/chat
↓
Application backend
|
| Streaming AI request
↓
LLM provider
|
| Chunk 1
| Chunk 2
| Chunk 3
↓
Application backend
|
| Forward chunks
↓
BrowserKeeping the AI request on the server also allows the application to protect private API keys, authenticate users, enforce quotas, and apply application-specific security controls.
Common Streaming Protocols
HTTP supports streaming response bodies, and web applications can use several mechanisms to transport incremental data. The appropriate choice depends on the application and the provider's API.
| Technology | Typical Use |
|---|---|
| HTTP streaming | General streaming response bodies |
| Server-Sent Events | One-way server-to-browser event streams |
| ReadableStream | Incrementally reading response data in browser JavaScript |
| WebSocket | Persistent two-way communication |
For a normal chatbot where the browser sends a request and the server progressively returns generated text, an HTTP streaming response or Server-Sent Events can often be sufficient. WebSockets are more useful when the application requires persistent bidirectional communication.
Streaming With the Fetch API
Modern browsers can read a streamed HTTP response through the Fetch API. The response body exposes a ReadableStream that can be consumed incrementally.
const response = await fetch("/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: input,
}),
});
if (!response.ok) {
throw new Error("Request failed");
}
const reader = response.body?.getReader();
if (!reader) {
throw new Error("Response is not readable");
}
The reader can then be used to retrieve chunks until the stream finishes.
const decoder = new TextDecoder();
let result = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
result += decoder.decode(value, { stream: true });
console.log(result);
}The chunks returned by the network are not guaranteed to correspond to complete words, sentences, or model tokens. Application code should therefore treat them as arbitrary pieces of a byte stream unless the chosen protocol defines a higher-level message format.
Streaming From a Next.js Backend
In a Next.js application, a server route can return a Web Streams API Response. A simplified example demonstrates the general architecture without depending on a specific AI provider SDK.
export async function POST(request: Request) {
const body = await request.json();
const message = body.message;
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
controller.enqueue(
encoder.encode("Hello ")
);
controller.enqueue(
encoder.encode("from the AI stream!")
);
controller.close();
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/plain; charset=utf-8",
},
"Cache-Control": "no-cache",
});
}In a real application, the values passed to controller.enqueue would come from the AI provider's streaming response rather than hard-coded strings.
Forwarding Provider Chunks
The backend can consume the provider's stream and immediately forward useful content to the client. The server should avoid unnecessarily buffering the complete response before sending it.
LLM provider
|
| generated chunk
↓
Backend reads chunk
|
| immediately forwards
↓
Browser receives chunk
↓
UI updatesIf an intermediate layer buffers the response, the browser may receive several chunks at once. This can remove much of the perceived benefit of streaming.
Handle Partial Chunks Correctly
One of the most important implementation details is that network chunks do not necessarily align with logical messages. A single word may be split between two chunks, while one chunk may contain several logical pieces.
Expected text:
"Streaming responses are useful."
Possible network chunks:
"Stream"
"ing res"
"ponses are use"
"ful."The client should append chunks rather than assuming that every chunk can be rendered as an independent complete message. If the provider uses a structured streaming protocol, parse its events according to the documented format.
Update the Chat UI Incrementally
A common chatbot pattern is to create an empty assistant message as soon as generation begins and append incoming content to that message.
setMessages((current) => [
...current,
{ role: "assistant", content: "" },
]);
// For every incoming chunk:
setMessages((current) => {
const messages = [...current];
const last = messages[messages.length - 1];
if (last?.role === "assistant") {
last.content += chunk;
}
return messages;
});For complex applications, updating state for every tiny chunk may cause excessive rendering. Batching updates or using a dedicated streaming state mechanism can improve frontend performance.
Handle Stream Completion
The client needs to know when the response has finished. Depending on the streaming protocol, completion may be represented by the end of the HTTP stream or by a dedicated event.
- Mark the assistant message as complete.
- Re-enable the input controls.
- Stop the loading indicator.
- Store the completed response if persistence is required.
- Record usage information when available.
Handle Errors During Streaming
Streaming introduces an additional failure mode: the connection can fail after some content has already reached the browser.
User request
↓
Chunk 1 → received
Chunk 2 → received
Chunk 3 → received
Chunk 4 → connection failsThe UI should preserve content that was already received and clearly indicate that generation was interrupted. Automatically retrying the entire request can result in duplicate output and additional API usage.
Cancel an LLM Stream
Users may want to stop generation before the model finishes. The browser can use AbortController to cancel its fetch request.
const controller = new AbortController();
const response = await fetch("/api/chat", {
method: "POST",
signal: controller.signal,
body: JSON.stringify({ message: input }),
});
// Stop the request
controller.abort();For cancellation to save downstream resources, the backend should also propagate cancellation to the AI provider when the provider and runtime support it. Otherwise, disconnecting the browser may not immediately stop the model request.
Streaming and Conversation History
Streaming does not change how conversation context works. The model still needs the appropriate previous messages, instructions, retrieved information, or other context when generating the response.
The difference is how the generated output is transported. The request can contain the same context as a non-streaming request while the response is delivered incrementally.
Streaming Does Not Solve Context Limits
A common misconception is that streaming somehow allows an application to process unlimited conversations. It does not. The model still has a context limit, and all input sent with the request consumes part of that context.
- Limit unnecessary conversation history.
- Summarize older messages when appropriate.
- Retrieve relevant information instead of sending everything.
- Keep system instructions concise.
- Monitor input and output usage.
Streaming and Cost
Streaming generally does not make generated tokens free. If the model generates the same output, the underlying usage can be similar whether the response is streamed or returned as one completed response.
Streaming can indirectly improve application efficiency by allowing users to stop generation when they have received enough information. However, the actual cost impact depends on whether cancellation reaches the provider in time and how the provider bills interrupted requests.
Security Considerations
A streaming endpoint needs the same security controls as a normal AI endpoint. Streaming does not make authentication or authorization unnecessary.
- Keep provider API keys server-side.
- Authenticate users when required.
- Validate incoming messages.
- Apply rate limits.
- Enforce usage quotas.
- Authorize access to stored conversations.
- Do not stream sensitive internal data accidentally.
- Avoid exposing provider errors or credentials.
If a chatbot can call tools during generation, additional authorization and validation are required. A model should not be allowed to perform sensitive operations simply because it requested them.
Buffering Can Break Streaming
Even when the AI provider streams correctly, an application may accidentally buffer the data somewhere between the model and the browser. Reverse proxies, middleware, framework configuration, or application code can delay delivery.
AI provider
|
| chunks
↓
Backend
|
| chunks
↓
Proxy / CDN / middleware
|
| buffered response
↓
BrowserWhen implementing streaming in production, verify the complete network path rather than testing only the AI provider's SDK.
Streaming With Server-Sent Events
Server-Sent Events, or SSE, provide a standardized way for a server to send a sequence of events to a browser over an HTTP connection. SSE is designed for one-way server-to-client communication, which fits many AI generation scenarios.
event: chunk
data: Hello
event: chunk
data: world
event: done
data: true
SSE can be convenient when the application benefits from explicit event types, but a plain streaming response can be simpler when the application only needs to forward text chunks.
When to Use Streaming
- AI chatbots with long responses.
- Coding assistants.
- Long-form text generation.
- Interactive writing applications.
- Summarization tools.
- AI agents that produce visible progress.
- Applications where perceived latency is important.
When Streaming May Not Be Necessary
- Short classification results.
- Small structured responses.
- Background processing where the user does not wait for output.
- Operations where the complete result must be validated before anything is displayed.
- Very simple prototypes where implementation simplicity is more important than progressive rendering.
Streaming is a transport and user-experience technique, not a requirement for every AI request.
Common Streaming Mistakes
- Assuming every network chunk is a complete token or word.
- Buffering the entire response before returning it.
- Ignoring stream errors after partial output.
- Retrying automatically without considering duplicate generation.
- Failing to cancel the downstream provider request.
- Updating frontend state too frequently.
- Exposing private provider information through streamed errors.
- Forgetting authentication and rate limits.
- Assuming streaming reduces total model generation time.
- Not testing streaming through the actual production proxy or CDN.
Best Practices
- Keep the AI provider request on the server.
- Forward generated content as soon as practical.
- Use a documented streaming format.
- Parse chunks according to the selected protocol.
- Treat network chunks as arbitrary boundaries.
- Show partial output immediately in the UI.
- Provide a stop-generation action for long responses.
- Handle interrupted streams without losing received content.
- Propagate cancellation when supported.
- Monitor time to first token and total response time.
- Apply authentication, rate limiting, and usage controls.
- Test the complete network path in production-like conditions.
Frequently Asked Questions
Does streaming make an LLM generate responses faster?
Not necessarily. Streaming mainly changes how generated output is delivered. Users can see the first content earlier, but the model may still require roughly the same amount of time to generate the complete response.
What is the difference between streaming and non-streaming LLM responses?
A non-streaming request returns the completed response as a whole, while a streaming request delivers the response progressively as it is generated. Streaming usually improves perceived responsiveness but requires more complex client and server handling.
Can I stream an LLM response with fetch()?
Yes. Modern browsers can consume a streamed HTTP response using the Fetch API and ReadableStream. The client reads incoming chunks and updates the interface as data becomes available.
Can an LLM stream be cancelled?
Yes. The browser can cancel its request using AbortController. For the cancellation to stop downstream model work, the backend should also propagate the cancellation when the AI provider and server environment support it.
Does streaming reduce AI API costs?
Streaming itself generally does not reduce the amount of generated output or its associated usage. It can potentially reduce waste when users stop generation early, but the actual billing behavior depends on the provider and when cancellation occurs.
Why do several streamed chunks sometimes arrive together?
Intermediate network layers can buffer data, and network chunk boundaries are not guaranteed to match model tokens or application messages. Production streaming should therefore be tested across the entire network path.
Conclusion
Streaming LLM responses is one of the most useful techniques for improving the perceived responsiveness of AI applications. Instead of waiting for the model to finish, the application can display generated content progressively as it becomes available.
The implementation requires more than simply enabling streaming at the AI provider. The backend must forward data without unnecessary buffering, the frontend must correctly process partial chunks, and the application must handle completion, errors, cancellation, authentication, rate limits, and usage tracking.
For chatbots and other interactive AI applications, streaming is often worth the additional complexity. For short or background operations, a normal request-response flow may be simpler and entirely sufficient.