🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Real-time AI UX

Learn how to implement real-time text streaming in your web apps. Master the Fetch Streams API, partial markdown rendering, and the essential UX patterns like auto-scroll that make AI responses feel instant.

Total XP: 0|💻 frontend XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core AI logic.

Quick Quiz //

Why does streaming an AI response improve the user experience even though the model takes the same total time to finish?


🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Listen up. If you're building modern applications, understanding Real-time AI UX is non-negotiable. This is where simple logic turns into intelligent behavior.

1Why AI Responses Need to Stream, Not Just Load

AI responses take time. To avoid making the user wait, we use Real-time Content Rendering—also known as Streaming.

A large language model doesn't generate its full answer instantly — it produces output token by token, and a multi-paragraph response can genuinely take several seconds to finish generating in full. If your UI waits for the entire response before showing anything, the user stares at a blank loading state for that entire duration.

Streaming changes what the user experiences without changing how long the model actually takes to finish: instead of one long wait followed by a wall of text, the response appears progressively as each chunk arrives, so the user starts reading within a fraction of a second. This is a perceived-performance technique, not a raw-speed one — the total generation time is the same, but it no longer feels like dead time.

+
// Example
console.log("Streaming AI response chunks...");
localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

2Reading Chunks With the Fetch API's Body Stream

Streaming sends data piece by piece as the model generates it. In React, we can capture these chunks using the Fetch API's body stream.

response.body is a ReadableStream, and calling .getReader() gives you direct control over consuming it chunk by chunk instead of waiting for response.json() or response.text() to resolve only once the entire body has arrived. The while (true) loop calls reader.read() repeatedly, each call resolving with the next available chunk (or done: true once the stream ends).

Each chunk arrives as a Uint8Array of raw bytes, not text — that's what new TextDecoder().decode(value) is for, converting the binary chunk into a readable string. Appending each decoded chunk onto the previous result via setResult(prev => prev + chunk) is what produces the incremental, typewriter-like rendering the user sees.

+
const response = await fetch('/api/ai', { stream: true });
const reader = response.body.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = new TextDecoder().decode(value);
  setResult(prev => prev + chunk);
}
localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

3Safely Rendering Partial Markdown Mid-Stream

Markdown rendering is essential for AI outputs. Since text arrives in chunks, we need a renderer that can handle partial markdown safely.

LLMs commonly format their answers with markdown — headings, bold text, code blocks, lists — and users expect that formatting to render, not show up as raw asterisks and backticks. But mid-stream, the text is frequently in an incomplete state: a bold marker (**) might have opened without its closing pair yet, or a code fence might be mid-block. Naively rendering that incomplete markdown as HTML can produce broken or flickering output.

ReactMarkdown handles this gracefully by re-parsing the full accumulated text on every update rather than trying to patch previous output — since it always works from the complete string received so far, an unterminated markdown construct just renders as plain text until its closing marker arrives, instead of corrupting the whole render.

+
import ReactMarkdown from 'react-markdown';

return (
  
{streamingText}
);
localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

4Keeping the Newest Streamed Text in View

Auto-scrolling: When text streams down the page, you should automatically scroll to keep the newest content in view.

As a streaming response grows, it can push past the visible viewport well before the model finishes generating — without auto-scroll, the user would need to manually scroll down repeatedly just to keep reading new text as it appears, which defeats the point of a smooth streaming experience.

The useEffect here re-runs on every change to streamingText, scrolling a sentinel element at the bottom of the response into view each time new text arrives. In a real chat interface, this needs a small refinement beyond what's shown: if the user manually scrolls up to re-read earlier text, auto-scroll should pause until they scroll back to the bottom, or it will fight the user's own scroll input.

+
useEffect(() => {
  bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [streamingText]);
localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

5What You've Unlocked: Responses That Feel Instant

Real-time rendering mastered! Your AI responses now feel fast and alive.

With chunked reading, incremental markdown rendering, and auto-scroll working together, an AI feature that technically takes several seconds to fully generate now feels responsive from the very first moment content appears — the perceived latency, which is what users actually judge an app by, has dropped dramatically even though the underlying model speed hasn't changed at all.

This combination — stream, render incrementally, keep the latest content visible — is the standard pattern behind essentially every modern AI chat interface, and it transfers directly to any future provider or model you integrate, since it depends only on the response being deliverable as a stream.

+

Streaming: Active

localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

6What's Next: Loading States and AI UX Polish

Next, we'll refine the experience with Loading States and optimized UX for AI.

Streaming solves the 'is anything happening' problem once tokens start arriving, but there's still a gap before the very first chunk shows up — the time spent waiting on the network request and the model's initial response.

The next lesson covers designing that pre-stream loading state (and other AI-specific UX details, like disabling input during generation and showing clear error states) so the experience feels considered and intentional from the first click, not just once streaming is underway.

+

UX Next

localhost:3000
Browser Preview
Execution Context
AI logic processed successfully.

7Step-by-Step Breakdown

AI responses take time. To avoid making the user wait, we use Real-time Content Rendering—also known as Streaming.

Streaming sends data piece by piece as the model generates it. In React, we can capture these chunks using the Fetch API's body stream.

Markdown rendering is essential for AI outputs. Since text arrives in chunks, we need a renderer that can handle partial markdown safely.

Checkpoint: Why is streaming preferred over waiting for the full response in AI apps?

  • It uses less data
  • It drastically reduces 'Perceived Latency' by showing text immediately

Auto-scrolling: When text streams down the page, you should automatically scroll to keep the newest content in view.

Real-time rendering mastered! Your AI responses now feel fast and alive.

Next, we'll refine the experience with Loading States and optimized UX for AI.

Check Real Streaming Render Safety. Finish checking whether it's safe to render a partial streamed response, or if it's mid-way through an unclosed code block.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Don't Spam Screen Readers With Every Streamed Chunk

An aria-live region re-announces its content on every change — wiring it directly to a fast-updating streamingText state causes a screen reader to attempt to re-read the entire growing response dozens of times per second, which is unusable. Update the live region on a debounce or only once streaming completes, and use aria-busy to indicate the in-progress state instead.

<div aria-live="polite" aria-busy={isStreaming}>{isStreaming ? 'Generating…' : finalText}</div>

SEO Implications

  • 1

    Streamed Client-Side Content Renders After Crawlers Typically Check the Page

    Text that arrives via a client-side fetch stream is not present in the server-rendered HTML — search crawlers that don't wait for and execute that streaming logic will see an empty or incomplete response area. If the streamed content is meant to be indexed, generate a non-streamed, fully server-rendered version for the initial page load.

Best Practices

Re-Parse the Full Accumulated Text, Don't Patch Previous Output

When rendering streamed markdown, feed the renderer the complete text received so far on every update rather than trying to incrementally append parsed HTML. This avoids broken rendering from markdown constructs that are still mid-stream (like an unclosed bold marker or code fence).

Let Auto-Scroll Yield to Manual User Scrolling

Forcing scrollIntoView() on every chunk update fights a user who has manually scrolled up to re-read earlier text. Track whether the user is currently at the bottom of the scroll container and only auto-scroll when they are.

Frequent Bugs

THE BUG

Decoding each streamed chunk independently with a fresh TextDecoder instance, which breaks multi-byte UTF-8 characters that happen to be split across two chunk boundaries, producing garbled text (e.g. broken emoji or accented characters).

THE FIX

Reuse a single TextDecoder instance across the whole stream (with { stream: true } passed to .decode()) so it can correctly buffer and reassemble multi-byte characters that span chunk boundaries.

Real-World Examples

Streaming Chat Response With Incremental Markdown

A documentation assistant streams its answer token-by-token from an API route, decoding each chunk and appending it to component state, while ReactMarkdown re-renders the growing string on every update so formatted code blocks and lists build up smoothly as the response arrives.

const reader = response.body.getReader();
const decoder = new TextDecoder();
let text = '';
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  text += decoder.decode(value, { stream: true });
  setStreamingText(text);
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Not reading error messages carefully

Uncaught TypeError: Cannot read properties of undefined (reading 'length') // Solution: Ensure the variable you are calling .length on is initialized as a string or an array, not undefined.

The Solution //

Most of the time, the compiler or interpreter tells you exactly what line caused the crash and why. Read stack traces from the top down to identify the root cause.

The Error //

Hardcoding sensitive credentials

// Wrong const API_KEY = 'sk-123456789'; // Correct const API_KEY = process.env.API_KEY;

The Solution //

Never hardcode API keys, passwords, or secrets in your source code. Use environment variables (.env files) to keep them secure and out of version control.

Lesson Glossary

[01]Streaming

Transferring data in a continuous flow, allowing the receiver to process it piece by piece.

Code Preview
Continuous Flow

[02]Perceived Latency

How fast a system feels to a user, regardless of actual technical processing time.

Code Preview
UX Feel

[03]TextDecoder

A browser API used to convert raw binary data (Uint8Array) into human-readable strings.

Code Preview
Bytes to Text

[04]Typewriter Effect

A UI pattern where text appears character-by-character or word-by-word to mimic typing.

Code Preview
AI Signature

Continue Learning