# Generative UI

Render the agent's step rows, cards, checklists, comparisons, charts, maps, and tabs in your own app.

Canonical page: https://kortix.com/docs/sdk/genui

The agent answers in markdown. When a project turns on **Generative UI**, the agent may add
` ```openui ` blocks for structured content: a ranked recommendation, a comparison, a chart from
tool data, places on a map. Short answers, explanations, and code stay prose. A guide, a trip, or a
shortlist reads as short paragraphs with one block per section.

## How the agent learns the blocks

The catalog ships as the `kortix-genui` system skill, not as system-prompt text. With the `genui`
[project flag](/docs/project#feature-flags) on, every new session lists the skill by name and a
one-line description. The agent loads the full catalog only when an answer needs a block, so a turn
without UI pays nothing for it. With the flag off, the skill is hidden. An agent with an explicit
`skills:` list in `kortix.yaml` sees the skill only when the list names `kortix-genui`.

## Install

Generative UI uses two optional peer dependencies. Install them before you import any
`@kortix/sdk/genui` entry; without them the import fails:

```bash
npm i zod @openuidev/lang-core@0.3.1
```

`@openuidev/lang-core` sends one pseudonymous PostHog event at install time. Set
`OPENUI_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1` to turn it off. Runtime telemetry is off unless
you opt in.

| Import | Holds | Loads the peers |
|---|---|---|
| `@kortix/sdk/genui/fence` | `genuiVersionFromClassName`, `genuiVersionOf`, `splitGenui`: fence detection | No |
| `@kortix/sdk/genui` | `parseGenui`, `genuiToMarkdown`, `genuiBlockToMarkdown`, the catalog | Yes |
| `@kortix/sdk/genui/react` | `GenuiBlock`, `defaultGenuiComponents` | Yes |

The root `@kortix/sdk` and `@kortix/sdk/react` entries never load the peers. Check a fence with
`@kortix/sdk/genui/fence` in your main bundle, and load the renderer lazily once a block exists.

## Render blocks

`@kortix/sdk/genui/react` exports `GenuiBlock`, a headless renderer. You pass a component for each
block name, or `defaultGenuiComponents` for unbranded HTML defaults.

| Prop | Meaning |
|---|---|
| `code` | The fence body so far |
| `version` | `genuiVersionFromClassName(className)` or `genuiVersionOf(tag)` |
| `streaming` | This block's fence is still open while the message streams. Pass `false` once the fence closes, so a finished block settles while the reply goes on |
| `components` | Block name → your component (`GenuiComponentProps`) |
| `renderMarkdown` | Your markdown renderer, used for fallbacks. Pass a stable function (module constant or `useCallback`): a new function re-renders the block's markdown parts and node views |
| `renderPending` | A node the model has not finished, while the block streams. Default: nothing. Return a fixed-height box for heavy nodes (a chart, a table) so the layout does not jump when the node settles |
| `enabled` | `false` renders the block as markdown (a viewer setting) |
| `onSettled` | One event per block: outcome, component names, timing. Never content |

A Checklist keeps its ticks in the component only: nothing is stored or sent.

## Plain text hosts

`genuiToMarkdown(text)` replaces every block with deterministic markdown. Charts become tables with a
`Source:` line; maps become place lists; tabs and accordions expand.
Blocks inside blockquotes and list items convert too. For a reply that is still streaming, pass
`genuiToMarkdown(text, { streaming: true })`: the unfinished last block shows no "cut off" note.
Steps become a numbered list; a Checklist becomes a task list (`- [ ]`).

A host on an older `@kortix/sdk` does not know a block added later (Steps and Checklist arrived after
v1). It shows *This content could not be displayed.* in that block's place, not the block's markdown.
Update the SDK to render or convert new blocks.

## Blocks

Stack, Card, Stat, StatRow, Table, Compare, RankedList, BarChart, LineChart, PieChart, Map, Steps,
Checklist, Tabs, Accordion, Badge, Callout, Image, Link. Limits (counts, lengths) are enforced by the SDK; a block that
breaks a limit is dropped and its siblings still render.

## Safety

Blocks hold data, never code. Every link and image URL must be absolute `http` or `https`. Generated
UI cannot run tools or change state.
