streamUI function for streaming React Server Components
The streamUI function from the AI SDK RSC package allows you to stream React Server Components from the server to the client. It is useful when you want to go beyond raw text and stream components to the client in real-time. The function works similarly to AI SDK Core APIs like streamText, supporting the same model interfaces. The streamUI function must always return a React component.
streamUI basic syntax with tools
The streamUI function accepts an object with properties: model (the AI model to use), prompt (the user's request), text (a function that renders plain text responses as a React component), and tools (an object containing tool definitions). Each tool has a description, inputSchema (a Zod schema), and generate (an async generator function returning a React component).
streamUI tool generate function with async generators
The generate function in a streamUI tool must be an async generator function (using function*). It allows yielding intermediate components (like loading states) before returning the final component. You can yield a LoadingComponent first, then await async operations, and finally return the resolved component. This enables showing loading UI while data is being fetched.
streamUI example with weather tool
const result = await streamUI({
model: openai('gpt-4o'),
prompt: 'Get the weather for San Francisco',
text: ({ content }) => <div>{content}</div>,
tools: {
getWeather: {
description: 'Get the weather for a location',
inputSchema: z.object({
location: z.string(),
}),
generate: async function* ({ location }) {
yield <LoadingComponent />;
const weather = await getWeather(location);
return <WeatherComponent weather={weather} location={location} />;
},
},
},
});
return result.value;
This example demonstrates streaming a weather component to the client using streamUI with a tool that yields a loading component before returning the final weather component.
Server Action implementation for streamUI in Next.js
Create a Server Action file (e.g., app/actions.tsx) with the 'use server' directive. Define the Server Action as an async function that calls streamUI and returns result.value. The Server Action must return a ReactNode. Helper components (LoadingComponent, WeatherComponent) and async functions (getWeather) should be defined in the same file before the Server Action function.
Client component for calling streamUI Server Action
Create a client component (marked with 'use client' directive) that uses React state to store the streamed component. On form submission, call the Server Action function (just like a regular function call). The returned ReactNode is then stored in state using setComponent and rendered in the JSX.
Difference between streamText and streamUI
streamText is an AI SDK Core function that streams text responses from the model. streamUI is an AI SDK RSC function that streams React Server Components from the server to the client. While streamText returns text tokens, streamUI allows the model to decide whether to call tools that return React components or fall back to returning text (which can be rendered as a component via the text handler).
streamUI tool interaction flow
When streamUI is called, the model receives the prompt and tool descriptions. If the model decides a tool is relevant to the user's request, it generates a tool call. The streamUI function then executes the tool's generate function, which can yield intermediate components (like loading states) and finally return the resolved component. If no tool is relevant, the model returns text, which is passed to the text handler function to render as a component.
AI SDK RSC is experimental
The AI SDK RSC is currently experimental. For production use, Vercel recommends using AI SDK UI instead. A migration guide is available for migrating from RSC to UI.
DirectChatTransport overview and use cases
DirectChatTransport is a transport that directly communicates with an Agent in-process, without going through HTTP. It is useful for server-side rendering scenarios, testing without network, and single-process applications. Unlike DefaultChatTransport which sends HTTP requests to an API endpoint, DirectChatTransport invokes the agent's stream() method directly and converts the result to a UI message stream.
DirectChatTransport import
Import DirectChatTransport from the 'ai' package using: import { DirectChatTransport } from "ai"
DirectChatTransport constructor parameters
DirectChatTransport constructor accepts the following parameters: (1) agent (type: Agent, required) - The Agent instance to use for generating responses. The agent will be called with stream() for each message. (2) options (type: CALL_OPTIONS, optional) - Options to pass to the agent when calling it. These are agent-specific options defined when creating the agent. (3) originalMessages (type: UIMessage[], optional) - The original messages. If provided, persistence mode is assumed, and a message ID is provided for the response message. (4) generateMessageId (type: IdGenerator, optional) - Generate a message ID for the response message. If not provided, no message ID will be set for the response message. (5) messageMetadata (type: function taking { part: TextStreamPart } and returning METADATA | undefined, optional) - Extracts message metadata that will be sent to the client. Called on start and finish events. (6) sendReasoning (type: boolean, optional, defaults to true) - Send reasoning parts to the client. (7) sendSources (type: boolean, optional, defaults to false) - Send source parts to the client. (8) sendFinish (type: boolean, optional, defaults to true) - Send the finish event to the client. Set to false if using additional streamText calls that send additional data. (9) sendStart (type: boolean, optional, defaults to true) - Send the message start event to the client. Set to false if using additional streamText calls and the message start event has already been sent. (10) onError (type: function taking error: unknown and returning string, optional, defaults to () => 'An error occurred.') - Process an error, e.g. to log it. Return the error message to include in the data stream.
DirectChatTransport.sendMessages() method parameters
The sendMessages() method accepts the following parameters: (1) chatId (type: string) - Unique identifier for the chat session. (2) trigger (type: 'submit-message' | 'regenerate-message') - The type of message submission - either new message or regeneration. (3) messageId (type: string | undefined) - ID of the message to regenerate, or undefined for new messages. (4) messages (type: UIMessage[]) - Array of UI messages representing the conversation history. (5) abortSignal (type: AbortSignal | undefined) - Signal to abort the request if needed. (6) headers (type: Record<string, string> | Headers, optional) - Additional headers (ignored by DirectChatTransport). (7) body (type: object, optional) - Additional body properties (ignored by DirectChatTransport). (8) metadata (type: unknown, optional) - Custom metadata (ignored by DirectChatTransport).
DirectChatTransport.sendMessages() return value
The sendMessages() method returns Promise<ReadableStream<UIMessageChunk>> - a stream of UI message chunks that can be processed by the chat UI.
DirectChatTransport.reconnectToStream() behavior
DirectChatTransport does not support reconnection since there is no persistent server-side stream to reconnect to. The reconnectToStream() method always returns Promise<null>.
DirectChatTransport basic usage example
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent({
model: openai('gpt-4o'),
instructions: 'You are a helpful assistant.',
});
export default function Chat() {
const { messages, sendMessage, status } = useChat({
transport: new DirectChatTransport({ agent }),
});
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.role === 'user' ? 'User: ' : 'AI: '}
{message.parts.map((part, index) =>
part.type === 'text' ? <span key={index}>{part.text}</span> : null,
)}
</div>
))}
<button onClick={() => sendMessage({ text: 'Hello!' })}>Send</button>
</div>
);
}
DirectChatTransport with agent tools example
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const weatherTool = tool({
description: 'Get the current weather',
inputSchema: z.object({
location: z.string().describe('The city and state'),
}),
execute: async ({ location }) => {
return `The weather in ${location} is sunny and 72°F.`;
},
});
const agent = new ToolLoopAgent({
model: openai('gpt-4o'),
instructions: 'You are a helpful assistant with access to weather data.',
tools: { weather: weatherTool },
});
export default function Chat() {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({ agent }),
});
// ... render chat UI with tool results
}
DirectChatTransport with custom agent options example
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent<{ userId: string }>({
model: openai('gpt-4o'),
prepareCall: ({ options, ...rest }) => ({
...rest,
providerOptions: {
openai: { user: options.userId },
},
}),
});
export default function Chat({ userId }: { userId: string }) {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({
agent,
options: { userId },
}),
});
// ... render chat UI
}
DirectChatTransport with reasoning example
import { useChat } from '@ai-sdk/react';
import { DirectChatTransport, ToolLoopAgent } from 'ai';
import { openai } from '@ai-sdk/openai';
const agent = new ToolLoopAgent({
model: openai('o1-preview'),
});
export default function Chat() {
const { messages, sendMessage } = useChat({
transport: new DirectChatTransport({
agent,
sendReasoning: true,
}),
});
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'text') {
return <p key={index}>{part.text}</p>;
}
if (part.type === 'reasoning') {
return (
<pre key={index} style={{ opacity: 0.6 }}>
{part.text}
</pre>
);
}
return null;
})}
</div>
))}
</div>
);
}
Server Actions with structured output workflow
Server Actions are React Server Component features that allow calling server-side functions directly from frontend code. In this pattern, Server Actions use generateText with Output schemas to: (1) Accept user input from frontend, (2) Call AI model with system prompt and context, (3) Return structured data matching Zod schema, (4) Send structured output back to frontend for rendering. This allows tight integration between AI-generated content and UI updates.