Streaming
Streaming components handle real-time AI responses with smooth visual transitions. They work with any streaming source — the AI SDK, Server-Sent Events, or WebSockets.
Overview
When an AI model generates a response, tokens arrive one at a time. Streaming components buffer these tokens and render them progressively, providing visual feedback that feels natural and responsive.
Streaming text
The simplest streaming pattern uses the AI SDK's useChat hook with Streamdown:
components/chat-message.tsx
"use client"
import { useChat } from "@ai-sdk/react"
import { Streamdown } from "streamdown"
import { code } from "@streamdown/code"
import { math } from "@streamdown/math"
export function ChatInterface() {
const { messages, input, handleInputChange, handleSubmit, isLoading } =
useChat()
return (
<div>
{messages.map((message) => (
<div key={message.id}>
{message.role === "assistant" ? (
<Streamdown
plugins={{ code, math }}
animated={isLoading && message === messages.at(-1)}
>
{message.content}
</Streamdown>
) : (
<p>{message.content}</p>
)}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="Ask a question..."
/>
</form>
</div>
)
}With Streamdown
Streamdown is purpose-built for streaming. Its animated prop controls whether new content animates in or appears instantly:
tsx
// Animated during streaming
<Streamdown animated={isStreaming} plugins={{ code, math }}>
{partialContent}
</Streamdown>
// Static after complete
<Streamdown animated={false} plugins={{ code, math }}>
{completeContent}
</Streamdown>Handles unterminated syntax
Streamdown gracefully handles unterminated code blocks, incomplete markdown syntax, and partial LaTeX expressions during streaming.
Loading states
Provide visual feedback while waiting for the first token:
components/typing-indicator.tsx
export function TypingIndicator() {
return (
<div className="flex items-center gap-1 px-3 py-2">
<div className="h-2 w-2 rounded-full bg-muted-foreground/40 animate-bounce" />
<div className="h-2 w-2 rounded-full bg-muted-foreground/40 animate-bounce [animation-delay:150ms]" />
<div className="h-2 w-2 rounded-full bg-muted-foreground/40 animate-bounce [animation-delay:300ms]" />
</div>
)
}