Skip to content

GenUI

Prompt and schema

Teach a model the catalog with a generated system prompt, or hold it to the format with a JSON Schema for structured outputs.

Generate the system prompt

genuiPrompt writes the format rules, the binding and expression rules, every component with its props and descriptions, the accessibility and safety rules, and worked JSON examples. Send it as the system message to any provider.

Exampletsx
import { genuiPrompt } from "@comp0/genui";

const system = genuiPrompt({
  preamble: "You are a travel assistant.",
  rules: ["Prices are in euros."],
});
genuiPrompt options
PropTypeDescription
preamblestringThe opening paragraph. Defaults to a sentence about composing interfaces from JSON.
rulesreadonly string[]Rules added after the built-in ones.
examplesreadonly (JsonValue | string)[]Complete example responses added after the built-in ones, as objects or JSON text.
catalogreadonly CatalogEntry[]The components a response may use. Defaults to the built-in catalog.
rootstringThe component a response is. Defaults to Stack.
toolsRecord<string, { description, input }>The host tools a Query or a call step may use, such as those given to runTool. Without any, Query is left out.
slotsRecord<string, string>The host views a Slot may place, by name, each with what it shows. Without any, Slot is left out.

The built-in rules

These rules keep what a model composes accessible, safe, and stable while it streams. They are exported as genuiPromptRules, and the built-in examples as genuiPromptExamples; tests validate every example so they cannot rot.

  • Accessibility is required: every input has a visible label and a unique name; every chart and table has a title or caption; every image has alt text describing it; buttons and links say what they do; keep heading levels in order.
  • Never rely on color alone. State a trend, status, or warning in words as well (a chart description, a Text with a tone, an Alert).
  • Use Alert only for information the person must act on; use Text for ordinary explanations.
  • Plain text only: Markdown and HTML in text props are shown literally. Link only to http(s), mailto:, tel:, relative, or #fragment URLs.
  • Use components only when they help the person act or compare. A short answer needs just a Text inside a Stack.
  • Put controls in a Form with a clear submitLabel. Use RadioGroup for five or fewer options and Select for more, and give a sensible default value when you know one.
  • Pick the data component by purpose: Table for exact values, BarChart or ColumnChart to compare, LineChart or AreaChart for change over time, PieChart for parts of a whole (six slices at most), Meter for a value in a range, ProgressBar for a task.
  • Use Output for results that depend on the person's choices (a price, a total, a conversion). Bind the controls ({"$bind": "seats", "initial": 5}) and write the Output value as {"$expr": "seats * 12"}; it recalculates as they change the controls. Never compute such a value yourself.
  • Use Comparison for choosing between options (plans, products): one feature per row with a value per option, true or false for included or not, and recommended only when you can say why.
  • Use CitedText when claims come from sources: put [1]-style markers after each claim and list every source once, in order. Never invent a source or a URL.
  • End with Suggestions (two to four short follow-up replies) when the person would likely continue; use CopyButton only next to the visible text it copies.
  • Streaming: write type first, then every other prop, and children (or a list of items with children) last; a prop written after the children may be ignored. Put the content people read first at the top, so the interface appears top-down while you write.

Read the shipped prompt

The prompt for the built-in catalog also ships pre-generated as @comp0/genui/genui.prompt.md, and this site serves the same file so coding assistants can read it. A test fails when the file is stale.

Constrain the output with a schema

responseJsonSchema returns the recursive JSON Schema (draft 2020-12) of one root component. Node is any component and every component has a $defs entry, so containers nest each other.

With strict: true every property is required, optional ones become nullable, additionalProperties is false, and the keywords strict mode rejects are removed. The validator treats null as a left-out optional prop, so a response written to the strict schema renders as is. Rules a schema cannot express, such as unsafe URLs, expression syntax, and unknown binding names, are still checked by the validator.

OpenAI structured outputtsx
const response = await client.chat.completions.create({
  model,
  messages,
  response_format: {
    type: "json_schema",
    json_schema: { name: "ui", strict: true, schema: responseJsonSchema({ strict: true }) },
  },
});
responseJsonSchema options
PropTypeDescription
strictbooleanShape the schema for strict structured outputs.
catalogreadonly CatalogEntry[]The components a response may use. Defaults to the built-in catalog.
rootstringThe component at the root of the schema. Defaults to Stack.