v0.1
The original OpenUI Lang specification for declarative UI generation in chat responses.
This is the v0.1 specification, the original language for generating static UI from LLM output. For the latest version with reactive state, data queries, and interactive apps, see v0.5.
OpenUI Lang is the output format the LLM generates. It is a compact, declarative, line-oriented DSL designed specifically for streaming and token efficiency.
While you rarely write this language manually, understanding the syntax is helpful for debugging raw LLM outputs or building custom parsers.
Syntax Overview
The language consists of a series of assignment statements. Every line binds a unique identifier to an expression.
root = Root([header, chart]) // 1. Entry Point
header = Header("Q4 Revenue", "YTD") // 2. Component Call
chart = BarChart(labels, [s1, s2]) // 3. Forward Reference
labels = ["Jan", "Feb", "Mar"] // 4. Data Definition
s1 = Series("Product A", [10, 20, 30])
s2 = Series("Product B", [5, 15, 25])Core Rules
- One statement per line:
identifier = Expression - Root Entry Point: The first statement must assign to the identifier
root(or call the Root component). If missing, nothing renders. - Top-Down Generation: Statements are generally written top-down (Layout -> Components -> Data) to maximize perceived performance during streaming.
- Positional Arguments: Arguments are mapped to component props by position, defined by the order of keys in your Zod schema.
Expressions & Types
OpenUI Lang supports a strict subset of JavaScript values.
| Type | Syntax | Example |
|---|---|---|
| Component Call | Type(arg1, arg2) | Header("Title", "Subtitle") |
| String | "text" | "Hello world" |
| Number | 123, 12.5, -5 | 42 |
| Boolean | true / false | true |
| Null | null | null |
| Array | [a, b, c] | ["Jan", "Feb", "Mar"] |
| Object | {key: val} | {variant: "info", id: 1} |
| Reference | identifier | myTableData |
Component Resolution
The parser maps Positional Arguments in OpenUI Lang to Named Props in React using your Zod definitions.
The Mapping Logic
The order of keys in your z.object schema defines the expected argument order.
1. The Schema (Zod)
const HeaderSchema = z.object({
title: z.string(), // Position 0
subtitle: z.string().optional(), // Position 1
});2. The Output (OpenUI Lang)
h1 = Header("Dashboard", "Overview")3. The Result (React Props)
{
"title": "Dashboard",
"subtitle": "Overview"
}Critical implications for component design
- Key order in z.object is the API contract. Changing key order breaks all existing LLM outputs.
- Required props must come before optional props in the schema, since trailing optional args can be omitted.
- The LLM learns positions from the auto-generated system prompt. If the prompt and schema disagree (e.g., after a schema change without regenerating the prompt), output will be garbled.
Optional Arguments
Optional arguments (trailing) can be omitted.
// Valid: subtitle is undefined
h1 = Header("Dashboard")Streaming & Hoisting
OpenUI Lang allows Forward References (Hoisting). An identifier can be used as an argument before it is defined in the stream. This is critical for progressive rendering.
How it works
root = Root([table]) // "table" is referenced here...
// ... (network latency) ...
table = Table(rows) // ...but defined here.- Step 1: The renderer parses
root. It seestableis undefined. It renders a Skeleton/Placeholder for the table. - Step 2: The
tabledefinition arrives. The renderer updates the React tree, replacing the skeleton with the actualTablecomponent. - Step 3:
rowsarrives → Table fills in with data.
Complete Syntax Example
A complex example showing nesting, arrays, and mixed types.
root = Root([nav, dashboard])
nav = Navbar("Acme Corp", [link1, link2])
link1 = Link("Home", "/")
link2 = Link("Settings", "/settings")
dashboard = Section([kpi_row, main_chart])
kpi_row = Grid([stat1, stat2])
stat1 = StatCard("Revenue", "$1.2M", "up")
stat2 = StatCard("Users", "450k", "flat")
main_chart = LineChart(
["Mon", "Tue", "Wed"],
[Series("Visits", [100, 450, 320])]
)