# Generative UI

> Turn model output into React interfaces with a local parser, component library, streaming renderer, and chat shell.

Source: https://ai.honestui.com/docs/generative

Generative UI lets a model choose the content and structure of an interface from components you allow. The model returns a text program in Honest AI Lang. Honest AI parses that program and renders it with Honest AI and Honest UI components.

For example, a response can contain a chart, a form, or a set of tabs instead of only a paragraph. You control the available components, the data they can request, and what happens when someone acts on the result.

## Try it locally [#try-it-locally]

Choose **Layout**, **Form**, **Chart**, or **Tabs**. Edit the source to change the rendered result. **Replay stream** sends the example to the renderer a little at a time so you can see how partial output behaves.

```tsx
"use client";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import {
  Renderer,
  honestLibrary,
  type HonestAIError,
} from "@/registry/generative";
export const generativeSamples = {
  Layout:
    'root = Stack([heading, card])\nheading = InlineHeader("Trip checklist", "Example content — no model is connected.")\ncard = Card([TextContent("## Before you leave\\n- Check your booking\\n- Pack your charger"), TagBlock(["Travel", "Checklist"])])',
  Form: 'root = Stack([form])\nform = Form("contact", buttons, [emailField])\nemailField = FormControl("Email", Input("email", "you@example.com", "email", { required: true, email: true }), "Use a valid email address.")\nbuttons = Buttons([Button("Send", @ToAssistant("Send contact form"), "primary")])',
  Chart:
    'root = Stack([title, chart])\ntitle = InlineHeader("Example sales", "Illustrative data.")\nchart = BarChart(["Jan", "Feb", "Mar"], [Series("Orders", [12, 18, 15])], "grouped")',
  Tabs: 'root = Stack([tabs])\ntabs = Tabs([first, second])\nfirst = TabItem("summary", "Summary", [TextContent("A generated interface using Honest UI tabs.")])\nsecond = TabItem("details", "Details", [TextContent("Tab through the controls or use the arrow keys.")])',
};
export function GenerativeUIDemo() {
  const [source, setSource] = React.useState(generativeSamples.Layout);
  const [response, setResponse] = React.useState(source);
  const [streaming, setStreaming] = React.useState(false);
  const [errors, setErrors] = React.useState<HonestAIError[]>([]);
  const [action, setAction] = React.useState("");
  React.useEffect(() => {
    if (!streaming) return;
    let cursor = 0;
    const timer = window.setInterval(() => {
      cursor += 16;
      setResponse(source.slice(0, cursor));
      if (cursor >= source.length) {
        setStreaming(false);
        window.clearInterval(timer);
      }
    }, 40);
    return () => window.clearInterval(timer);
  }, [streaming, source]);
  return (
    <div
      data-slot="generative-playground"
      className="grid w-full min-w-0 gap-5 p-4"
    >
      <div className="flex flex-wrap gap-2">
        {Object.entries(generativeSamples).map(([name, sample]) => (
          <Button
            key={name}
            variant="secondary"
            disabled={streaming}
            onClick={() => {
              setSource(sample);
              setResponse(sample);
              setAction("");
            }}
          >
            {name}
          </Button>
        ))}
      </div>
      <div className="grid gap-2">
        <Label htmlFor="generative-source">Honest AI Lang source</Label>
        <Textarea
          id="generative-source"
          value={source}
          disabled={streaming}
          rows={7}
          className="font-mono text-xs"
          onChange={(event) => {
            setSource(event.target.value);
            setResponse(event.target.value);
          }}
        />
      </div>
      <div className="flex items-center gap-3">
        <Button
          onClick={() => {
            setResponse("");
            setStreaming(true);
          }}
          disabled={streaming}
        >
          Replay stream
        </Button>
        {streaming && (
          <Button
            variant="secondary"
            onClick={() => {
              setStreaming(false);
              setResponse(source);
            }}
          >
            Stop replay
          </Button>
        )}
        <p role="status" className="m-0 text-sm text-muted-foreground">
          {streaming
            ? "Replaying example tokens…"
            : "Local preview. No model request is sent."}
        </p>
      </div>
      <div className="min-w-0 rounded-md border p-4">
        <Renderer
          response={response}
          library={honestLibrary}
          isStreaming={streaming}
          onError={setErrors}
          onAction={(event) => setAction(JSON.stringify(event, null, 2))}
        />
      </div>
      {!streaming && errors.length > 0 && (
        <div role="alert" className="text-sm text-destructive">
          {errors.map((error, i) => (
            <p key={i}>{error.message}</p>
          ))}
        </div>
      )}
      {action && (
        <div>
          <p role="status">
            Action received locally. Nothing was submitted to a server.
          </p>
          <pre className="max-w-full overflow-x-auto text-xs">{action}</pre>
        </div>
      )}
    </div>
  );
}

```

This playground runs locally. It does not call a model. The Form example validates the email field and displays its action below the preview; it does not submit the email anywhere.

## Explore more examples [#explore-more-examples]

The [examples gallery](/docs/generative/examples) includes saved state, query loading and retry, a custom delivery component, and parser diagnostics. Each demo includes the React source and runs without a model service.

Use [Language](/docs/generative/language) to learn the program format, [State and forms](/docs/generative/state) to keep an interface interactive, and [Data and tools](/docs/generative/tools) to connect permitted reads and writes.

## Build your first integration [#build-your-first-integration]

1. [Install the renderer](/docs/generative/installation) and its styles.
2. [Render a fixed program](/docs/generative/renderer#render-a-fixed-program) to check your setup before connecting a model.
3. [Generate a model prompt](/docs/generative/server#generate-the-model-prompt) from the same library used by your renderer.
4. [Stream the response](/docs/generative/renderer#stream-a-response) into that renderer, or [connect the chat shell](/docs/generative/chat) if you need conversation history and a composer.

## Choose what to install [#choose-what-to-install]

| Your task                                                   | Use                                                                         |
| ----------------------------------------------------------- | --------------------------------------------------------------------------- |
| Render a generated form, chart, or layout inside your app   | `Renderer` with `honestLibrary`                                             |
| Display generated interfaces in a conversation              | `GenerativeChat`, which uses `honestChatLibrary` by default                 |
| Give the model your allowed components and check its output | The JSON library spec and local server helpers                              |
| Add your own product component                              | [Extend the component library](/docs/generative/library#extend-the-library) |

Both libraries use a `Stack` root. The application `Stack` is a flex container; the chat `Stack` is a plain vertical list inside a Markdown reply. Keep the model prompt, server spec, and client library matched; a program generated for one library may contain components the other does not accept.

## What runs here [#what-runs-here]

The parser, React renderer, form state, chat state, and transport adapters are local source. Generative UI has no upstream package dependency or default vendor service. Its controls share Honest UI's components and theme; Markdown, code, records, and the conversation shell reuse Honest AI.

Your application supplies the model endpoint, credentials, persistent storage, and tool implementations. Rendering a generated button does not save data or grant permission. Its action handler decides what to do, and your server authorizes any operation on protected data.

The renderer interprets the registered language; it does not evaluate generated JavaScript. Optional server repair calls only the callback you supply. The installed source retains its required MIT copyright and license notice.
