RSC and streamUI (experimental)

Official status: AI SDK RSC is currently experimental. For production, use AI SDK UI (useChat + a route handler). See Migrating from RSC to UI in the official docs.

@ai-sdk/rsc targets frameworks with React Server Components (mainly Next.js App Router). The model can stream React components from the server instead of plain text. That pattern is often called generative UI.


Versus AI SDK UI

AI SDK UI (recommended)AI SDK RSC (experimental)
TransportPOST /api/chat + UI Message StreamServer Actions + RSC payload
ClientuseChat, you render partsInsert the ReactNode the server returns
Tool UIDraw from tool-* partsThe tool generate returns a component
StabilityProduction defaultMay break in minor releases

Use RSC only when the model should pick which card or chart to render and your team accepts an experimental API. Ordinary chat should not take this path.


Core APIs (names only)

FunctionRole
streamUICall a model and allow an RSC reply
createAIClient–server context for UI state / AI state
useUIState / useAIStateuseState-like hooks for those two stores
useActionsCall Server Actions from the client
createStreamableUI / createStreamableValuePush UI or serializable values by hand

Install with pnpm add @ai-sdk/rsc (you still need ai and a provider package).


Minimal streamUI

Tools look like streamText, but the runner is generate and it must return a React node. An async generator can yield a loading state, then return the final component.

app/actions.tsx:

'use server';

import { streamUI } from '@ai-sdk/rsc';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

export async function streamComponent() {
  const result = await streamUI({
    model: openai('gpt-5.1'), // direct example; Gateway strings also work
    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 <div>getting weather...</div>;
          const weather = '82°F';
          return (
            <div>
              The weather in {location} is {weather}
            </div>
          );
        },
      },
    },
  });

  return result.value;
}

If no tool applies, the model uses text—you still return a component. streamUI must return a React node.

The client page calls the Server Action like any async function:

'use client';

import { useState } from 'react';
import { streamComponent } from './actions';

export default function Page() {
  const [component, setComponent] = useState<React.ReactNode>();

  return (
    <form
      onSubmit={async (e) => {
        e.preventDefault();
        setComponent(await streamComponent());
      }}
    >
      <button type="submit">Stream Component</button>
      <div>{component}</div>
    </form>
  );
}

Practical notes

  • Secrets stay in Server Actions, same rule as route handlers.
  • Multi-turn chat needs createAI to keep AI state and UI state in sync. That is heavier than useChat. Use official templates (Gemini Chatbot, Generative UI with RSC) instead of inventing a state machine.
  • Do not mash HarnessAgent or a normal streamText chat onto the same route as streamUI.

After this chapter, spend time on useChat and Tools. Card-style UI can also be rendered from tool-* parts on the UI path—RSC is not required.


Next

评论