Elements

Elements · Renderers

Generative UI

A styled component library for rendering structured generative UI output.

fig. 01

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 init

Then 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.

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

app/toolkit.ts
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

app/page.tsx
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.

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.

app/toolkit.ts
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

MemberParametersDescription
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 PresentToolA 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 PromptUserToolThe 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.