React Server Function Streams
This pattern is useful for sending partial responses to the client, such as when you’re waiting for data from an external API, like an AI model.
Example
First create a server function that returns a stream. In this example we’re using Cloudflare’s AI, and we’re streaming the response back to the client.
"use server";
export async function sendMessage(prompt: string) { console.log("Running AI with Prompt:", prompt); const response = await env.AI.run("@cf/meta/llama-4-scout-17b-16e-instruct", { prompt, stream: true, }); return response as unknown as ReadableStream;}
Now on the client component, we can use the consumeEventStream
function to parse the chunks whilst keeping the UI updated.
"use client";
import { sendMessage } from "./functions";import { useState } from "react";import { consumeEventStream } from "rwsdk/client";
export function Chat() { const [message, setMessage] = useState(""); const [reply, setReply] = useState(""); const [isLoading, setIsLoading] = useState(false); const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault();
setIsLoading(true);
setReply(""); (await sendMessage(message)).pipeTo( consumeEventStream({ onChunk: (event) => { setReply((prev) => { if (event.data === "[DONE]") { setIsLoading(false); return prev; } return (prev += JSON.parse(event.data).response); }); }, }) ); };
return ( <div> <div>{reply}</div>
<form onSubmit={onSubmit}> <input type="text" value={message} placeholder="Type a message..." onChange={(e) => setMessage(e.target.value)} /> <button type="submit" disabled={message.length === 0 || isLoading}> {isLoading ? "Sending..." : "Send"} </button> </form> </div> );}
For a working example, please see the Chat example.