Skip to content

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.

Stack props
PropTypeDescription
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.
wrapbooleanLet a row wrap onto several lines.
childrenRequiredComponent[]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.

Grid props
PropTypeDescription
columnsnumberNumber of equal columns, 1 to 6; 2 by default.
gap"xs" | "sm" | "md" | "lg" | "xl"Space between cells; "md" by default.
childrenRequiredComponent[]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.

Card props
PropTypeDescription
titleRequiredstringHeading of the card; it also names the card for screen readers.
descriptionstringOne short supporting sentence under the title.
tone"default" | "muted" | "accent"Visual emphasis.
childrenRequiredComponent[]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.

Heading props
PropTypeDescription
textRequiredstringThe heading text.
levelnumberHeading 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.

Text props
PropTypeDescription
textRequiredstring | ExpressionPlain 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.

Image props
PropTypeDescription
srcRequiredimage URLhttp(s) or relative URL of the image. data: URLs are not rendered.
altRequiredstringText alternative describing what the image shows. Required; never a filename.
captionstringVisible caption under the image.

List

A bulleted or numbered list of short text items. Use it for steps, options, or highlights. Requires items.

List props
PropTypeDescription
itemsRequiredstring[] | BindingOne short text per item.
orderedbooleanNumber 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.

Button props
PropTypeDescription
labelRequiredstringVisible text of the button; say what happens when it is pressed.
variant"primary" | "secondary" | "danger""primary" for the main action; "danger" for destructive ones.
messagestringWhat 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.

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.

Suggestions props
PropTypeDescription
labelRequiredstringNames the group of replies, such as 'Follow-up questions'. Required.
itemsRequiredstring[]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.

CopyButton props
PropTypeDescription
labelRequiredstringVisible text saying what is copied, such as 'Copy install command'.
valueRequiredstringThe 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.

Form props
PropTypeDescription
nameRequiredstringShort identifier for the form, unique in the response.
titleRequiredstringAccessible name of the form. Add a Heading to show it.
submitLabelstringText 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.
childrenRequiredComponent[]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.

TextField props
PropTypeDescription
labelRequiredstringVisible label naming the field. Required.
nameRequiredstringIdentifier of the field, unique in its form.
valuestring | BindingInitial text, or a binding {"$bind": name} the field reads and writes.
requiredbooleanThe person must fill this in.
placeholderstringExample input; never a substitute for the label.
inputType"text" | "email" | "tel" | "url"Kind of text: "text" (default), "email", "tel", or "url".
descriptionstringHelp 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.

TextArea props
PropTypeDescription
labelRequiredstringVisible label naming the field. Required.
nameRequiredstringIdentifier of the field, unique in its form.
valuestring | BindingInitial text, or a binding {"$bind": name}.
requiredbooleanThe person must fill this in.
placeholderstringExample input; never a substitute for the label.
descriptionstringHelp 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.

NumberField props
PropTypeDescription
labelRequiredstringVisible label naming the field. Required.
nameRequiredstringIdentifier of the field, unique in its form.
valuenumber | BindingInitial number, or a binding {"$bind": name} the field reads and writes; empty when omitted.
minnumberSmallest allowed number.
maxnumberLargest allowed number.
stepnumberAmount one step changes the number; 1 by default.
requiredbooleanThe person must fill this in.
descriptionstringHelp 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.

Select props
PropTypeDescription
labelRequiredstringVisible label naming the field. Required.
nameRequiredstringIdentifier 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.
valuestring | BindingInitially chosen option value, or a binding {"$bind": name} the field reads and writes.
requiredbooleanThe person must choose one.
descriptionstringHelp 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.

RadioGroup props
PropTypeDescription
labelRequiredstringVisible question or label naming the group. Required.
nameRequiredstringIdentifier 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.
valuestring | BindingInitially chosen option value, or a binding {"$bind": name} the group reads and writes.
requiredbooleanThe person must choose one.
descriptionstringHelp 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.

CheckboxGroup props
PropTypeDescription
labelRequiredstringVisible label naming the group. Required.
nameRequiredstringIdentifier 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.
valuestring[] | BindingOption values that start checked, or a binding {"$bind": name}.
descriptionstringHelp 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.

Checkbox props
PropTypeDescription
labelRequiredstringVisible text next to the checkbox. Required.
nameRequiredstringIdentifier of the field, unique in its form.
checkedboolean | BindingStarts checked, or a binding {"$bind": name} the checkbox reads and writes.
requiredbooleanMust 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.

Switch props
PropTypeDescription
labelRequiredstringVisible text next to the switch, naming the setting. Required.
nameRequiredstringIdentifier of the field, unique in its form.
checkedboolean | BindingStarts 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.

Slider props
PropTypeDescription
labelRequiredstringVisible label naming the setting. Required.
nameRequiredstringIdentifier of the field, unique in its form.
minnumberLowest value; 0 by default.
maxnumberHighest value; 100 by default.
valuenumber | BindingInitial value, or a binding {"$bind": name} the slider reads and writes; min by default.
stepnumberAmount 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.

DatePicker props
PropTypeDescription
labelRequiredstringVisible label naming the date. Required.
nameRequiredstringIdentifier of the field, unique in its form.
valuestring | BindingInitial date as "YYYY-MM-DD", or a binding {"$bind": name}.
requiredbooleanThe person must pick a date.
descriptionstringHelp 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.

Tabs props
PropTypeDescription
labelstringAccessible 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? }.

Accordion props
PropTypeDescription
multiplebooleanAllow 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.

Disclosure props
PropTypeDescription
summaryRequiredstringVisible text that toggles the details.
openbooleanStarts open.
childrenRequiredComponent[]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).

Table props
PropTypeDescription
captionRequiredstringWhat the table shows; it names the table for screen readers.
columnsRequiredstring[]Column headings, left to right.
rowsRequired((number | string | boolean | null)[])[] | BindingOne 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.

BarChart props
PropTypeDescription
titleRequiredstringWhat the chart shows; it names the chart. Required.
dataRequired{ label: string, value: number }[] | BindingThe bars, each { label, value }.
descriptionstringOne sentence stating the main takeaway.
categoryLabelstringAxis label for the categories; "Category".
valueLabelstringAxis label for the numbers; "Value".
unitstringText 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.

ColumnChart props
PropTypeDescription
titleRequiredstringWhat the chart shows; it names the chart. Required.
dataRequired{ label: string, value: number }[] | BindingThe bars, each { label, value }.
descriptionstringOne sentence stating the main takeaway.
categoryLabelstringAxis label for the categories; "Category".
valueLabelstringAxis label for the numbers; "Value".
unitstringText 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.

LineChart props
PropTypeDescription
titleRequiredstringWhat the chart shows; it names the chart. Required.
dataRequired{ x: number, y: number }[] | BindingThe points, each { x, y }.
descriptionstringOne sentence stating the main takeaway.
xLabelstringAxis label for x; "X".
yLabelstringAxis label for y; "Y".
unitstringText 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).

AreaChart props
PropTypeDescription
titleRequiredstringWhat the chart shows; it names the chart. Required.
dataRequired{ x: number, y: number }[] | BindingThe points, each { x, y }.
descriptionstringOne sentence stating the main takeaway.
xLabelstringAxis label for x; "X".
yLabelstringAxis label for y; "Y".
unitstringText 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.

PieChart props
PropTypeDescription
titleRequiredstringWhat the chart shows; it names the chart. Required.
dataRequired{ label: string, value: number }[] | BindingThe slices, each { label, value } with value >= 0.
descriptionstringOne sentence stating the main takeaway.
categoryLabelstringHeading for the slice names; "Category".
valueLabelstringHeading for the numbers; "Value".
unitstringText 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.

Meter props
PropTypeDescription
labelRequiredstringWhat is measured. Required.
valueRequirednumber | ExpressionThe current measurement, or an expression such as {"$expr": "used / total * 100"}.
minnumberLowest possible value; 0 by default.
maxnumberHighest possible value; 100 by default.
lownumberValues below this are low.
highnumberValues above this are high.
optimumnumberThe ideal value.

ProgressBar

Progress of a task from 0 to 100 percent. Requires label; omit value when the amount is unknown.

ProgressBar props
PropTypeDescription
labelRequiredstringWhat is progressing. Required.
valuenumber | ExpressionPercent 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.

Output props
PropTypeDescription
labelRequiredstringNames the result, such as 'Monthly total'. Required.
valueRequirednumber | string | ExpressionThe result. Write {"$expr": "seats * 12"} so it recalculates as the person changes the controls bound to the names it reads.
unitstringText 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.

Comparison props
PropTypeDescription
captionRequiredstringWhat is compared; it names the table. Required.
optionsRequiredstring[]The things compared, such as plan names, left to right.
featuresRequired({ name: string, values: (number | string | boolean | null)[] })[]The rows, each { name, values }.
recommendedstringThe 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.

CitedText props
PropTypeDescription
textRequiredstringThe 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.

Alert props
PropTypeDescription
messageRequiredstringWhat the person needs to know, in a sentence or two.
tone"info" | "success" | "warning" | "danger""info" by default.
titlestringShort bold lead-in.

Separator

A divider line between sections. Takes no required arguments.

Separator props
PropTypeDescription
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.

Query props
PropTypeDescription
toolRequiredstringName of the host tool to call, from the list of tools.
argsobjectArguments for the tool; bind a value with {"$bind": name} and the query runs again whenever it changes.
intoRequiredstringName 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.

Slot props
PropTypeDescription
nameRequiredstringName 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.

A custom entrytsx
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 a ReactNode.
  • bindable(schema, description) declares a value a control can bind with $bind.
  • computed(schema, description) declares a value that may be an $expr expression.
  • 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 with children.