Elements · Renderers
Generative UI
A styled component library for rendering structured generative UI output.
Installation
npx shadcn@latest add "@assistant-ui/generative-ui"First time? Set up a runtime
Runtime components read their state from an assistant-ui runtime. Add one to an existing project:
npx assistant-ui@latest initThen wrap your app in a runtime provider:
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";
export default function App() {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: "/api/chat" }),
});
return (
<AssistantRuntimeProvider runtime={runtime}>
{/* your components */}
</AssistantRuntimeProvider>
);
}The installation guide covers new projects, templates, and API routes.
npx shadcn@latest add "@assistant-ui/generative-ui"Props-driven: no runtime or provider required.
styledGenerativeUILibrary is @assistant-ui/react-generative-ui's default component vocabulary (headings, text, cards, lists, and more) with one entry swapped: its Markdown component renders with react-markdown and GitHub-flavored markdown instead of dumping the raw source as plain text. With a runtime the library backs a tool the model calls to render structured UI; standalone you call renderGenerativeUI yourself with any spec built against the same library.
Getting started
The library only supplies components; something still has to expose them to the model and mount the result when it calls them. JSONGenerativeUI builds that bridge: a tool whose parameters come from the library's Zod schemas, and whose render draws the model's response through the library.
Build a toolkit around the library
import { defineToolkit } from "@assistant-ui/react";
import { JSONGenerativeUI } from "@assistant-ui/react-generative-ui";
import { styledGenerativeUILibrary } from "@/components/assistant-ui/elements/generative-ui";
const generative = new JSONGenerativeUI({ library: styledGenerativeUILibrary });
export default defineToolkit({
present: generative.present(),
});Register it on your runtime
import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
import toolkit from "./toolkit";
const config = AuiConfig({ tools: Tools({ toolkit }) });
const runtime = useChatRuntime(/* ... */); // or useLocalRuntime, etc.
<AssistantRuntimeProvider config={config} runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>;The model can now call present with a { $type, ...props } tree; it renders through styledGenerativeUILibrary, so a Markdown node comes out fully formatted instead of as raw text.
Standalone, skip the tool: call renderGenerativeUI with any spec built against the library.
Render a spec directly
import { renderGenerativeUI } from "@assistant-ui/react-generative-ui";
import { styledGenerativeUILibrary } from "@/components/assistant-ui/elements/generative-ui";
function Report() {
return renderGenerativeUI(
{ $type: "Markdown", value: "**Revenue** is up 12% this quarter." },
styledGenerativeUILibrary,
);
}Each node selects a component by $type and passes its other keys as props; nest further nodes under a children key.
Examples
See the override in action
defaultGenerativeUILibrary.Markdown renders value as plain text; styledGenerativeUILibrary.Markdown parses the same string as GitHub-flavored markdown. Both wrap the result in the same <div data-aui="markdown">, so any styling already targeting that attribute keeps applying.
import { defaultGenerativeUILibrary, renderGenerativeUI } from "@assistant-ui/react-generative-ui";
import { styledGenerativeUILibrary } from "@/components/assistant-ui/elements/generative-ui";
renderGenerativeUI({ $type: "Markdown", value: "**bold**" }, defaultGenerativeUILibrary); // <div data-aui="markdown">**bold**</div>
renderGenerativeUI({ $type: "Markdown", value: "**bold**" }, styledGenerativeUILibrary); // <div data-aui="markdown"><strong>bold</strong></div>Extend the library with your own component
Add to it with a plain object spread. No special wrapper is required for a library that is not split across a client and a server build.
import { JSONGenerativeUI } from "@assistant-ui/react-generative-ui";
import { styledGenerativeUILibrary } from "@/components/assistant-ui/elements/generative-ui";
import { z } from "zod";
const library = {
...styledGenerativeUILibrary,
Callout: {
description: "A highlighted note.",
properties: z.object({ text: z.string() }),
render: ({ text }: { text: string }) => <div data-aui="callout">{text}</div>,
},
};
const generative = new JSONGenerativeUI({ library });API reference
JSONGenerativeUI
| Member | Parameters | Description |
|---|---|---|
new JSONGenerativeUI(options) | { library: GenerativeUILibrary; actions?: ActionRegistry } | Builds the tool's parameter schema once, from the library's Zod properties. |
.present(options?) | { display?: "standalone" }, returns a PresentTool | A frontend tool: parameters are the library's schema, and calling it renders the model's { $type, ...props } tree through the library. display: "standalone" surfaces the result outside the chain-of-thought trace instead of inline. |
.promptUser() | none, returns a PromptUserTool | The same rendering, as a human-in-the-loop tool: the model pauses until the rendered UI supplies a result. |
Register the resulting tool the way you register any other: defineToolkit({ present: generative.present() }), then Tools({ toolkit }) into your AuiConfig.
renderGenerativeUI
| Parameter | Type | Description |
|---|---|---|
node | unknown | A { $type, ...props } tree, or an array of them. $type selects the component; every other key becomes a prop; a children key nests further nodes. |
library | GenerativeUILibrary | Looked up by $type. An unknown type logs a console error in development and renders nothing. |
context | { status: "streaming" | "done"; dispatch?(action) } | Optional, defaults to { status: "done" }. A component whose streamProperties is not true renders nothing while status is "streaming". |
styledGenerativeUILibrary itself takes no props: it is a GenerativeUILibrary value, the same shape as defaultGenerativeUILibrary, with its Markdown entry replaced.