experimental_StreamingReactResponse
The experimental_StreamingReactResponse
class allows you to stream React component responses.
The experimental_
prefix indicates that the API is not yet stable and may
change in the future without a major version bump.
It is currently only implemented from ai/react
's useChat
hook.
experimental_StreamingReactResponse(res: ReadableStream, options?: ResponseOptions): Response
The experimental_StreamingReactResponse
class is designed to facilitate streaming React responses in a server action environment. It can handle and stream both raw content and data payloads, including special UI payloads, through nested promises.
Parameters
res: ReadableStream
This parameter should be a ReadableStream
, which encapsulates the HTTP response's content. It represents the stream from which the response is read and processed.
options?: {ui?: Function, data?: experimental_StreamData}
This optional parameter allows additional configurations for rendering React components and handling streamed data.
The options object can include:
ui?: (message: {content: string, data?: JSONValue[] | undefined}) => UINode | Promise<UINode>
: A function that receives a message object withcontent
and optionaldata
fields. This function should return a React component (asUINode
) for each chunk in the stream. Thedata
attribute in the message is available when thedata
option is configured to include stream data.data?: experimental_StreamData
: An instance ofexperimental_StreamData
used to process and stream data along with the response.
Returns
The method returns a Promise<ReactResponseRow>
, which resolves to the next row of the React response. Each row contains a payload with UI components (ui
), raw content (content
), and a next
property pointing to the subsequent row or null
if it's the last row. This setup allows for continuous streaming and rendering of data in a React-based UI.
Example
Server-Side Implementation
This example highlights the usage of experimental_StreamingReactResponse
specifically within a server action context in a Next.js environment. It is crucial to note that this functionality is only available when used in server actions. For more details on server actions, visit Next.js Documentation on Server Actions (opens in a new tab).
In this server-side script, the handler
function interacts with the OpenAI API to process and stream chat completions. The responses are streamed using OpenAIStream
and subsequently managed by experimental_StreamingReactResponse
for dynamic React component rendering.
Server:
'use server';
import OpenAI from 'openai';
import { OpenAIStream, experimental_StreamingReactResponse, Message } from 'ai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY!,
});
export async function handler({ messages }: { messages: Message[] }) {
// Request the OpenAI API for the response based on the prompt
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages: messages as any,
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new experimental_StreamingReactResponse(stream, {
ui({ content }) {
return (
<div className="italic text-red-800">
{content} Visit Next.js docs at{' '}
<a
href="https://nextjs.org/docs"
target="_blank"
className="underline"
>
https://nextjs.org/docs
</a>
</div>
);
},
});
}
Client-Side Setup
On the client side, the useChat hook from ai/react is utilized to manage the chat interaction. The Chat
component below renders the chat interface, including input forms and message displays. It dynamically integrates the server-side stream, displaying messages and UI elements as they are received.
import { handler } from './action';
import { Chat } from './chat';
export const runtime = 'edge';
export default function Page() {
return <Chat handler={handler} />;
}
'use client';
import { useChat } from 'ai/react';
export function Chat({ handler }: { handler: any }) {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: handler,
});
return (
<div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
<ul>
{messages.map((m, index) => (
<li key={index}>
{m.role === 'user' ? 'User: ' : 'AI: '}
{m.role === 'user' ? m.content : m.ui}
</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input
className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl"
placeholder="What is the weather in New York?"
value={input}
onChange={handleInputChange}
autoFocus
/>
</form>
</div>
);
}