GenUI
Component catalog
Every component a model may compose and the props its schema declares, generated from the catalog that validates responses.
How to read the catalog
Each component lists the props its schemas declare. The same schemas validate responses and write the prompt and the JSON Schema, so this page, the validator, and what the model is told cannot disagree.
Binding accepts { "$bind": name, "initial"?: value }, Expression accepts { "$expr": "..." }, and Component[] is an array of component objects. A URL prop accepts only safe link or image URLs.
Layout
Stacks, grids, and cards arrange everything else; headings, text, images, and lists carry the reading content. Stack, Grid, and Card take any component as children, including each other.
Stack
Lays out components in order, vertically by default or in a row. Use it for the root and to group related components. Requires children.
| Prop | Type | Description |
|---|---|---|
direction | "column" | "row" | "column" (default) stacks vertically. |
gap | "xs" | "sm" | "md" | "lg" | "xl" | Space between children; "md" by default. |
align | "start" | "center" | "end" | "stretch" | Cross-axis alignment. |
wrap | boolean | Let a row wrap onto several lines. |
childrenRequired | Component[] | Components to lay out, in order. |
Grid
Places components in equal columns, for dashboards and side-by-side cards. Requires children; columns defaults to 2.
| Prop | Type | Description |
|---|---|---|
columns | number | Number of equal columns, 1 to 6; 2 by default. |
gap | "xs" | "sm" | "md" | "lg" | "xl" | Space between cells; "md" by default. |
childrenRequired | Component[] | Components to place in grid cells, in order. |
Card
A titled section that groups related content, such as one metric, one result, or one step. Requires a title and children.
| Prop | Type | Description |
|---|---|---|
titleRequired | string | Heading of the card; it also names the card for screen readers. |
description | string | One short supporting sentence under the title. |
tone | "default" | "muted" | "accent" | Visual emphasis. |
childrenRequired | Component[] | Content of the card. |
Heading
A heading that titles the content below it. Use it to structure a long response. Requires text; keep levels in order.
| Prop | Type | Description |
|---|---|---|
textRequired | string | The heading text. |
level | number | Heading level 1 to 6; 2 by default. Do not skip levels. |
Text
A paragraph of plain text. Use it for explanations around other components. Requires text.
| Prop | Type | Description |
|---|---|---|
textRequired | string | Expression | Plain text. Markdown and HTML are shown literally. May be {"$expr": "..."} over bound names. |
tone | "default" | "muted" | "success" | "warning" | "danger" | Meaning of the text; "default" unless it matters. |
size | "sm" | "md" | "lg" | Text size; "md" by default. |
Image
An image from an http(s) or relative URL. Requires src and alt text that describes the image; add a caption when the image needs explaining.
| Prop | Type | Description |
|---|---|---|
srcRequired | image URL | http(s) or relative URL of the image. data: URLs are not rendered. |
altRequired | string | Text alternative describing what the image shows. Required; never a filename. |
caption | string | Visible caption under the image. |
List
A bulleted or numbered list of short text items. Use it for steps, options, or highlights. Requires items.
| Prop | Type | Description |
|---|---|---|
itemsRequired | string[] | Binding | One short text per item. |
ordered | boolean | Number the items; bulleted by default. |
Actions
A Button sends its message back to your app, a Link navigates, and Suggestions offer quick replies that become the person's next message.
Button
A button that sends a message back to the assistant when pressed, for choices and next steps ("Show details", "Book it"). Inside a Form it sends that form's values. Requires a label that says what it does.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible text of the button; say what happens when it is pressed. |
variant | "primary" | "secondary" | "danger" | "primary" for the main action; "danger" for destructive ones. |
message | string | What the person is asking for, written as their message to the assistant. Defaults to the label. |
action | { call?: string, args?: object, into?: string, set?: string, value?: any, send?: string }[] | Steps to run in order when pressed, instead of sending the message: call a tool, set a value, send a message. Stops at the first that fails. |
Link
A link to a web page, email address, phone number, or section. Requires label text and an http(s), mailto:, tel:, relative, or #fragment href; other URLs are not linked.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible link text that makes sense out of context. |
hrefRequired | URL | http(s), mailto:, tel:, relative, or #fragment URL. Other schemes are not linked. |
Suggestions
Quick replies for likely next steps. Pressing one sends its text as the person's next message. Requires a label for the group and the list of replies; keep them short and few.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Names the group of replies, such as 'Follow-up questions'. Required. |
itemsRequired | string[] | Short replies the person can send as their next message, each a sentence or less. |
CopyButton
A button that copies text to the clipboard, such as a code snippet, command, or address. Show the text itself nearby, since the person cannot see what is copied. Requires a label that says what is copied and the value.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible text saying what is copied, such as 'Copy install command'. |
valueRequired | string | The exact text placed on the clipboard. |
Forms
Controls go in a Form with a clear submit label. Every control needs a visible label and a name that is unique in its form. Options are plain strings, or { "value", "label" } objects when the stored value differs from the text.
Form
Groups form controls and a submit button. When the person submits, the assistant receives their answers, described with the controls' labels. Requires name, title, and children; put controls (TextField, Select, ...) inside, and a Button inside only for secondary actions.
| Prop | Type | Description |
|---|---|---|
nameRequired | string | Short identifier for the form, unique in the response. |
titleRequired | string | Accessible name of the form. Add a Heading to show it. |
submitLabel | string | Text of the submit button; "Submit" by default. |
action | { call?: string, args?: object, into?: string, set?: string, value?: any, send?: string }[] | Steps to run in order on submit instead of sending the answers to the assistant: a call step without args receives the form's values by control name. Stops at the first that fails. |
childrenRequired | Component[] | The form controls and any explanatory components. |
TextField
A single-line text input (name, email, phone, URL). A form control. Always give it a visible label; it names the control for everyone. Requires label and name.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the field. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
value | string | Binding | Initial text, or a binding {"$bind": name} the field reads and writes. |
required | boolean | The person must fill this in. |
placeholder | string | Example input; never a substitute for the label. |
inputType | "text" | "email" | "tel" | "url" | Kind of text: "text" (default), "email", "tel", or "url". |
description | string | Help text shown under the field. |
TextArea
A multi-line text input for longer answers. A form control. Always give it a visible label; it names the control for everyone. Requires label and name.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the field. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
value | string | Binding | Initial text, or a binding {"$bind": name}. |
required | boolean | The person must fill this in. |
placeholder | string | Example input; never a substitute for the label. |
description | string | Help text shown under the field. |
NumberField
A numeric input with increase and decrease buttons, for quantities and amounts. A form control. Always give it a visible label; it names the control for everyone. Requires label and name; add min and max when there are limits.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the field. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
value | number | Binding | Initial number, or a binding {"$bind": name} the field reads and writes; empty when omitted. |
min | number | Smallest allowed number. |
max | number | Largest allowed number. |
step | number | Amount one step changes the number; 1 by default. |
required | boolean | The person must fill this in. |
description | string | Help text shown under the field. |
Select
A drop-down for choosing one of many options (more than five). A form control. Always give it a visible label; it names the control for everyone. Requires label, name, and options.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the field. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
optionsRequired | (string | { value: string, label?: string })[] | The choices: plain strings (shown and stored as written) or { value, label } when the stored value differs from the text. |
value | string | Binding | Initially chosen option value, or a binding {"$bind": name} the field reads and writes. |
required | boolean | The person must choose one. |
description | string | Help text shown under the field. |
RadioGroup
Choose exactly one of a few visible options (five or fewer). A form control. Always give it a visible label; it names the control for everyone. Requires label, name, and options.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible question or label naming the group. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
optionsRequired | (string | { value: string, label?: string })[] | The choices: plain strings (shown and stored as written) or { value, label } when the stored value differs from the text. |
value | string | Binding | Initially chosen option value, or a binding {"$bind": name} the group reads and writes. |
required | boolean | The person must choose one. |
description | string | Help text shown under the group. |
CheckboxGroup
Choose any number of a few visible options. A form control. Always give it a visible label; it names the control for everyone. Requires label, name, and options; the value is the list of checked option values.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the group. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
optionsRequired | (string | { value: string, label?: string })[] | The choices: plain strings (shown and stored as written) or { value, label } when the stored value differs from the text. |
value | string[] | Binding | Option values that start checked, or a binding {"$bind": name}. |
description | string | Help text shown under the group. |
Checkbox
A single yes/no choice such as accepting terms. The label is the visible text next to the box. Requires label and name.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible text next to the checkbox. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
checked | boolean | Binding | Starts checked, or a binding {"$bind": name} the checkbox reads and writes. |
required | boolean | Must be checked to submit. |
Switch
An on/off setting that applies right away, such as notifications. The label names the setting. Requires label and name.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible text next to the switch, naming the setting. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
checked | boolean | Binding | Starts on, or a binding {"$bind": name} the switch reads and writes. |
Slider
A number chosen along a range, when the exact value matters less than the rough amount. A form control. Always give it a visible label; it names the control for everyone. Requires label and name; min is 0 and max is 100 unless given.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the setting. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
min | number | Lowest value; 0 by default. |
max | number | Highest value; 100 by default. |
value | number | Binding | Initial value, or a binding {"$bind": name} the slider reads and writes; min by default. |
step | number | Amount one step changes the value; 1 by default. |
DatePicker
A calendar date. The value is "YYYY-MM-DD". A form control. Always give it a visible label; it names the control for everyone. Requires label and name.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Visible label naming the date. Required. |
nameRequired | string | Identifier of the field, unique in its form. |
value | string | Binding | Initial date as "YYYY-MM-DD", or a binding {"$bind": name}. |
required | boolean | The person must pick a date. |
description | string | Help text shown under the field. |
Disclosure
Tabs, accordions, and disclosures hold components inside their items; each tab or section has its own children array.
Tabs
Switches between parallel views of the same subject (for example per region or per plan). Requires tabs, each { label, children }; only one view is visible at a time.
| Prop | Type | Description |
|---|---|---|
label | string | Accessible name of the tab list. |
tabsRequired | { label: string, children: Component[] }[] | The tabs, each { label, children } where children are the components shown when the tab is selected. |
Accordion
A list of sections that expand one at a time (or several with multiple). Use it for FAQs and long, skimmable content. Requires items, each { title, children, open? }.
| Prop | Type | Description |
|---|---|---|
multiple | boolean | Allow several sections open at once. |
itemsRequired | { title: string, open?: boolean, children: Component[] }[] | The sections, each { title, open?, children }. |
Disclosure
One show/hide section for optional details. Requires summary and children; use Accordion for several.
| Prop | Type | Description |
|---|---|---|
summaryRequired | string | Visible text that toggles the details. |
open | boolean | Starts open. |
childrenRequired | Component[] | Details revealed when opened. |
Data
Tables show exact values, charts show shape, and results show what the person's choices add up to. Charts require a title and include a data table; Output shows a computed result; Comparison and CitedText support choosing and citing.
Table
A table for comparing items across attributes. Requires caption, columns, and rows; the first column should name each row. Use it instead of a chart when exact values matter. Large tables are cut off (24 columns, 800 cells).
| Prop | Type | Description |
|---|---|---|
captionRequired | string | What the table shows; it names the table for screen readers. |
columnsRequired | string[] | Column headings, left to right. |
rowsRequired | ((number | string | boolean | null)[])[] | Binding | One array of cells per row, in column order. The first cell names the row. |
BarChart
Horizontal bars comparing a number across categories, best for long labels or rankings. Requires title and data ({ label, value } objects); add a description with the takeaway. A data table is included for screen readers.
| Prop | Type | Description |
|---|---|---|
titleRequired | string | What the chart shows; it names the chart. Required. |
dataRequired | { label: string, value: number }[] | Binding | The bars, each { label, value }. |
description | string | One sentence stating the main takeaway. |
categoryLabel | string | Axis label for the categories; "Category". |
valueLabel | string | Axis label for the numbers; "Value". |
unit | string | Text appended to numbers, such as "%" or " ms". |
ColumnChart
Vertical columns comparing a number across a few short categories. Requires title and data ({ label, value } objects); add a description with the takeaway.
| Prop | Type | Description |
|---|---|---|
titleRequired | string | What the chart shows; it names the chart. Required. |
dataRequired | { label: string, value: number }[] | Binding | The bars, each { label, value }. |
description | string | One sentence stating the main takeaway. |
categoryLabel | string | Axis label for the categories; "Category". |
valueLabel | string | Axis label for the numbers; "Value". |
unit | string | Text appended to numbers, such as "%" or " ms". |
LineChart
A line showing how a number changes along an ordered axis such as time. Requires title and data ({ x, y } objects with increasing x); add a description with the takeaway.
| Prop | Type | Description |
|---|---|---|
titleRequired | string | What the chart shows; it names the chart. Required. |
dataRequired | { x: number, y: number }[] | Binding | The points, each { x, y }. |
description | string | One sentence stating the main takeaway. |
xLabel | string | Axis label for x; "X". |
yLabel | string | Axis label for y; "Y". |
unit | string | Text appended to y numbers, such as "%" or " ms". |
AreaChart
Like LineChart with the area below filled, for totals that accumulate. Requires title and data ({ x, y } objects with increasing x).
| Prop | Type | Description |
|---|---|---|
titleRequired | string | What the chart shows; it names the chart. Required. |
dataRequired | { x: number, y: number }[] | Binding | The points, each { x, y }. |
description | string | One sentence stating the main takeaway. |
xLabel | string | Axis label for x; "X". |
yLabel | string | Axis label for y; "Y". |
unit | string | Text appended to y numbers, such as "%" or " ms". |
PieChart
Parts of a whole, for up to six slices that sum to a meaningful total. Requires title and data ({ label, value } objects with values >= 0). Prefer BarChart for more slices.
| Prop | Type | Description |
|---|---|---|
titleRequired | string | What the chart shows; it names the chart. Required. |
dataRequired | { label: string, value: number }[] | Binding | The slices, each { label, value } with value >= 0. |
description | string | One sentence stating the main takeaway. |
categoryLabel | string | Heading for the slice names; "Category". |
valueLabel | string | Heading for the numbers; "Value". |
unit | string | Text appended to numbers, such as "%" or " ms". |
Meter
A value within a known range, such as disk usage or a score. Requires label and value; min is 0 and max is 100 unless given. Use ProgressBar for task progress.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | What is measured. Required. |
valueRequired | number | Expression | The current measurement, or an expression such as {"$expr": "used / total * 100"}. |
min | number | Lowest possible value; 0 by default. |
max | number | Highest possible value; 100 by default. |
low | number | Values below this are low. |
high | number | Values above this are high. |
optimum | number | The ideal value. |
ProgressBar
Progress of a task from 0 to 100 percent. Requires label; omit value when the amount is unknown.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | What is progressing. Required. |
value | number | Expression | Percent complete, 0 to 100, or an expression; omit when unknown. |
Output
A computed result shown next to the controls it depends on, such as a price or a total. Bind controls with {"$bind": name} and write the value as {"$expr": "..."} over those names, and it updates live. Requires label and value.
| Prop | Type | Description |
|---|---|---|
labelRequired | string | Names the result, such as 'Monthly total'. Required. |
valueRequired | number | string | Expression | The result. Write {"$expr": "seats * 12"} so it recalculates as the person changes the controls bound to the names it reads. |
unit | string | Text shown after a number, such as "%" or " per month". |
Comparison
Sets options side by side, such as plans or products, with a recommended one. Requires caption, options, and features; each feature has one value per option, true or false for included or not.
| Prop | Type | Description |
|---|---|---|
captionRequired | string | What is compared; it names the table. Required. |
optionsRequired | string[] | The things compared, such as plan names, left to right. |
featuresRequired | ({ name: string, values: (number | string | boolean | null)[] })[] | The rows, each { name, values }. |
recommended | string | The name of the option to recommend, exactly as written in options. |
CitedText
An answer whose claims point at numbered sources. Requires text with [1]-style markers and the list of sources ({ title, href?, note? }); the sources are listed under the text and each marker links to its source.
| Prop | Type | Description |
|---|---|---|
textRequired | string | The answer as plain text. Mark each claim with the number of its source in square brackets, such as [1] or [2][3]. Blank lines separate paragraphs. |
sourcesRequired | { title: string, href?: URL, note?: string }[] | The sources, each { title, href?, note? }, numbered from 1. |
Feedback
Alert is for information the person must act on; warnings and errors are announced at once.
Alert
A highlighted message: confirmation, information, a warning, or an error. Requires message; tone is info by default. Warnings and errors are announced immediately.
| Prop | Type | Description |
|---|---|---|
messageRequired | string | What the person needs to know, in a sentence or two. |
tone | "info" | "success" | "warning" | "danger" | "info" by default. |
title | string | Short bold lead-in. |
Separator
A divider line between sections. Takes no required arguments.
| Prop | Type | Description |
|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" by default. |
Host
Query and Slot connect the interface to your app. They are offered to the model only when you pass tools or slots to genuiPrompt, and they do something only when the renderer has a tool caller or the named view.
Query
Calls one of the host's tools and stores the result under a name, again whenever a bound argument changes, without asking the assistant. Draws nothing: bind a chart's data, a table's rows, or a list's items to the name to show the result. Requires tool and into.
| Prop | Type | Description |
|---|---|---|
toolRequired | string | Name of the host tool to call, from the list of tools. |
args | object | Arguments for the tool; bind a value with {"$bind": name} and the query runs again whenever it changes. |
intoRequired | string | Name to store the result under; show it with {"$bind": name} in a data prop such as a chart's data. |
Slot
Places one of the host's own views, such as a live preview, by name. The host's views read values bound by your controls. Requires name.
| Prop | Type | Description |
|---|---|---|
nameRequired | string | Name of a host view to place here, from the list of host views. |
Add your own components
defineEntry declares an entry as plain data: a name, a group, a description written for the model, its props, and the React component that renders them. Props are a shape: each key holds a Standard Schema that can also describe itself as JSON Schema (ArkType 2.1.28+, zod 4.2+, or Valibot through toStandardJsonSchema()), and a key ending in ? is optional. Pass the extended catalog to GenUI, genuiPrompt, and responseJsonSchema alike, and use describeCatalog to list it as plain data, as this page does. The built-ins come apart the same way: catalogSpecs is every built-in component as data with no renderer, safe to load on a server for prompts and schemas, and withComponents(catalogSpecs, { ...builtInComponents, Button: MyButton }) pairs specs with components by name, so you can swap one renderer and keep what the model is told; builtInComponents maps each built-in name to its React component.
While a response streams, a prop is checked against its JSON Schema so partial values still render. Once it is finished it must also pass the library's own validate, so formats and refinements count. Validators must be synchronous. A component receives only the props its schema declares, and each may be missing while the response streams, so render defensively.
import { catalog, defineEntry, GenUI, genuiPrompt } from "@comp0/genui";
import { type } from "arktype";
import { z } from "zod";
const Shout = defineEntry({
name: "Shout",
group: "Layout",
description: "Loud text. Requires text.",
props: {
text: type("string").describe("What to shout."),
"volume?": z.number().min(1).max(11).describe("How loud, 1 to 11."),
},
component: ({ text, volume }) => <strong data-volume={volume}>{text}</strong>,
});
const withShout = [...catalog, Shout];
<GenUI response={response} catalog={withShout} />;
genuiPrompt({ catalog: withShout });nodes(description)declares a slot for nested components; the component receives aReactNode.bindable(schema, description)declares a value a control can bind with$bind.computed(schema, description)declares a value that may be an$exprexpression.url("link" | "image", description)declares a URL the validator must vet.list(itemShape, description)declares a list whose items hold any of these, such as tabs withchildren.