Back to Copilotkit

Open Generative UI

docs/content/docs/(root)/generative-ui/open-generative-ui.mdx

1.57.16.7 KB
Original Source

import { Tabs, Tab } from "fumadocs-ui/components/tabs";

What is this?

Open Generative UI lets the agent generate complete, sandboxed UI on the fly — HTML, CSS, and JavaScript — and stream it live into the chat. The user sees the interface build in real time: styles apply first, then HTML streams in progressively, and finally JavaScript expressions execute one by one.

Key benefits:

  • No predefined components — the agent creates any UI it needs, on demand
  • Live streaming — HTML streams into a preview as it's generated
  • CDN libraries — the generated UI can load external libraries (Chart.js, D3, Three.js, etc.) via <script> tags
  • Secure sandboxing — content runs in an isolated iframe without same-origin access
  • Sandbox functions — expose host application functions to the sandboxed UI for two-way communication

Basic setup

Enable Open Generative UI by setting openGenerativeUI: true in your runtime configuration. That's it — the middleware handles the rest.

<Steps> <Step> ### Add the dependencies
```npm
npm install @copilotkit/react-ui @copilotkit/react-core @copilotkit/runtime
```
</Step> <Step> ### Configure the runtime
```typescript title="app/api/copilotkit/route.ts"
import {
  CopilotRuntime,
  createCopilotEndpoint,
  InMemoryAgentRunner,
} from "@copilotkit/runtime";
import { BuiltInAgent } from "@copilotkit/runtime/v2";
import { handle } from "hono/vercel";

const agent = new BuiltInAgent({
  model: "openai/gpt-5.4",
  prompt: "You are a helpful assistant.",
});

const runtime = new CopilotRuntime({
  agents: { default: agent },
  runner: new InMemoryAgentRunner(),
  // Enable Open Generative UI
  openGenerativeUI: true,
});

const app = createCopilotEndpoint({
  runtime,
  basePath: "/api/copilotkit",
});

export const GET = handle(app);
export const POST = handle(app);
```

You can also pass options instead of `true`:

```typescript
openGenerativeUI: {
  instructions: "Always use a dark color scheme.",
}
```

The `instructions` string is appended to the tool description, letting you customize what the agent generates.
</Step> <Step> ### Configure the frontend
```tsx title="app/page.tsx"
"use client";

import { CopilotKit } from "@copilotkit/react-core/v2";
import { CopilotChat } from "@copilotkit/react-core/v2";
import "@copilotkit/react-ui/v2/styles.css";

export default function Page() {
  return (
    <CopilotKit runtimeUrl="/api/copilotkit">
      <CopilotChat />
    </CopilotKit>
  );
}
```

No special frontend prop is needed — Open Generative UI rendering is built in.
</Step> </Steps>

That's it. Ask the agent to "build a calculator" or "show me a bar chart of sales data" and it will generate and stream the UI live.

Advanced: Sandbox functions

Sandbox functions let the generated UI call back into your host application. This enables two-way communication — for example, a generated settings panel that can toggle your app's theme, or a product card that adds items to your cart.

Defining sandbox functions

Pass sandboxFunctions to the openGenerativeUI prop on CopilotKit:

tsx
"use client";

import { CopilotKit, CopilotChat } from "@copilotkit/react-core/v2";
import "@copilotkit/react-ui/v2/styles.css";
import { useState } from "react";
import { z } from "zod";

export default function Page() {
  const [theme, setTheme] = useState<"light" | "dark">("light");

  return (
    <CopilotKit
      runtimeUrl="/api/copilotkit"
      openGenerativeUI={{
        sandboxFunctions: [
          {
            name: "setTheme",
            description: "Switch the app theme between light and dark mode.",
            parameters: z.object({
              mode: z.enum(["light", "dark"]).describe("The theme mode to set"),
            }),
            handler: async ({ mode }) => {
              setTheme(mode);
              return `Theme set to ${mode}`;
            },
          },
        ],
      }}
    >
      <CopilotChat />
    </CopilotKit>
  );
}

How sandbox functions work

Each sandbox function has:

PropertyTypeDescription
namestringFunction name, callable from the sandbox
descriptionstringTells the agent what this function does
parametersZod schemaValidates arguments passed from the sandbox
handler(args) => Promise<string>Runs in the host app, returns a result string

Inside the generated UI, the agent calls these functions via:

javascript
// Inside the sandboxed iframe
const result = await Websandbox.connection.remote.setTheme({ mode: "dark" });

The function descriptions are automatically included in the agent's context, so it knows which functions are available and how to use them.

Use cases

  • Theme toggling — generated UI controls your app's appearance
  • Cart / state management — product cards that add items to the host app's state
  • Navigation — generated UI that triggers route changes in the host app
  • Data fetching — sandbox calls the host to fetch data the iframe can't access directly (due to same-origin restrictions)

How streaming works

The agent generates parameters in a specific order optimized for the user experience:

  1. placeholderMessages — shown immediately while generating
  2. css — all styles generated first; the preview starts once CSS is complete
  3. html — streams live into the preview as it's generated
  4. jsFunctions — reusable helper functions injected before expressions
  5. jsExpressions — executed one by one; the user sees each take effect

The middleware parses the tool call arguments incrementally and emits activity events as each parameter completes, so the frontend can show progressive updates.

Using CDN libraries

The sandboxed iframe can load external libraries from CDNs. Include <script> or <link> tags in the generated HTML <head>:

html
<head>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
  <canvas id="myChart"></canvas>
</body>

Libraries like Chart.js, D3, Three.js, x-data-spreadsheet, and any other CDN-hosted library work out of the box.

Middleware API

If you prefer to apply the middleware to specific agents rather than globally, use OpenGenerativeUIMiddleware directly:

typescript
import { OpenGenerativeUIMiddleware } from "@copilotkit/runtime";

const agent = new BuiltInAgent({
  model: "openai/gpt-5.4",
  prompt: "You are a helpful assistant.",
}).use(
  new OpenGenerativeUIMiddleware({
    instructions: "Use a modern, minimal design.",
  }),
);