The default advice for LLM integrations is still to wait for the full JSON response and then update the UI. That choice costs you every bit of perceived performance you have. I have seen sites where a user stares at a spinner for 15 seconds while the model is busy “thinking.” AI response streaming is the fix, and in 2026 there is no reason left to skip it.
Ask a model for 2,000 words and the network is not your bottleneck. Token generation speed is. No AI implementation strategy, however well tuned, gets you around inference time. Streaming works around it a different way: the response goes out token by token as the model produces it.
SSE or WebSockets
People reach for WebSockets the moment they hear “real-time.” Unless the client and server genuinely need a constant two-way conversation, the way a complex multi-agent system might, WebSockets are overkill. You pay for them in state management and server overhead.
For most WordPress AI apps, Server-Sent Events (SSE) is the better fit. It runs one way, server to client, over plain HTTP. It is light, it reconnects on its own, and browsers support it through the MDN EventSource API. It is also how OpenAI and Claude run their own streaming endpoints.
The naive JSON fetch
Most code calls fetch(), waits for the response to resolve, then parses the JSON. That is fine for a 100ms API call. For a model that takes ten seconds to answer, it is a UX disaster.
// The "Naive" Approach - Don't do this for AI
async function fetchAIResponse(prompt) {
const response = await fetch('/wp-json/my-ai/v1/generate', {
method: 'POST',
body: JSON.stringify({ prompt })
});
const data = await response.json(); // Site hangs here for 10 seconds
document.getElementById('output').innerText = data.text;
}
Reading the response as a stream
Consume the response body as a stream instead. Calling getReader() on it lets you handle chunks of text as they arrive from the model.
// The Senior Dev Approach: Consuming a Stream
async function streamAIResponse(prompt) {
const response = await fetch('/wp-json/my-ai/v1/stream', {
method: 'POST',
body: JSON.stringify({ prompt })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let isDone = false;
while (!isDone) {
const { value, done } = await reader.read();
isDone = done;
const chunkValue = decoder.decode(value);
// Append tokens to UI in real-time
document.getElementById('output').innerText += chunkValue;
}
}
The content validation problem
Streaming costs you something, and what it costs is validation. You no longer get to check the content before the user sees it. If the model hallucinates or breaks a safety policy at token #400, your UI already showed the first 399.
A client’s chatbot once gave solid advice for a paragraph and then wandered off into nonsense halfway through. So if your app needs strict output guarantees, valid JSON or a clean toxicity check, you may be better off not streaming. At minimum, run a validation pass afterwards that can “undo” the output when it fails.
If AI response streaming is eating your dev hours, I can take it on. I have been wrestling with WordPress since the 4.x days.
When to stream and when to wait
Streaming is not a typing-effect gimmick. It is what makes a slow model feel usable. That does not mean putting stream: true on every request. Look at how long your outputs run and whether you need to validate before anything renders. Chatbot, stream it. Structured configuration file, wait for the whole payload.