Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ benchmarks/
`schema.json` and `system-prompt.txt` are generated by `library.toJSONSchema()` and `library.prompt()` in `@openuidev/react-ui`. If you add or change components, regenerate them:

```ts
import { defaultLibrary } from "@openuidev/react-ui";
import { openuiLibrary } from "@openuidev/react-ui";
import { writeFileSync } from "fs";

writeFileSync("schema.json", JSON.stringify(defaultLibrary.toJSONSchema(), null, 2));
writeFileSync("system-prompt.txt", defaultLibrary.prompt());
writeFileSync("schema.json", JSON.stringify(openuiLibrary.toJSONSchema(), null, 2));
writeFileSync("system-prompt.txt", openuiLibrary.prompt());
```

The parser (`createParser`) reads component definitions from the `$defs` section of this file to map positional arguments to named props.
4 changes: 2 additions & 2 deletions docs/app/(home)/components/FeaturesSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ interface Feature {
const FEATURES: Feature[] = [
{
title: "Performance Optimized",
description: "2.1x faster rendering than json-render",
description: "Up to 3.0x faster rendering than json-render",
iconPath: svgPaths.p7658f00,
},
{
title: "Token efficient",
description: "52% lesser tokens than json-render",
description: "Up to 67.1% lesser tokens than json-render",
iconPath: svgPaths.p2a8ddd80,
},
{
Expand Down
6 changes: 3 additions & 3 deletions docs/app/docs/openui-lang/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ export default function OpenUILangOverview() {
>
A2UI
</Link>{" "}
that uses ~52% fewer tokens than equivalent JSON structures. Define your component library
that uses up to 67.1% fewer tokens than equivalent JSON structures. Define your component library
with Zod schemas and parse LLM responses into renderable components.
</p>

<div className="flex flex-col gap-3 sm:flex-row">
<Button
href="/docs/openui-lang/quickstart"
text="Build with Default Library"
text="Build with OpenUI Library"
variant="primary"
/>
<Button
Expand All @@ -127,7 +127,7 @@ export default function OpenUILangOverview() {
<FeatureCard
icon={<Zap />}
title="Token Efficient"
description="Uses ~52% fewer tokens than equivalent JSON structures, significantly reducing inference cost and latency."
description="Uses up to 67.1% fewer tokens than equivalent JSON structures, significantly reducing inference cost and latency."
/>
<FeatureCard
icon={<Shield />}
Expand Down
4 changes: 2 additions & 2 deletions docs/app/playground/components/CatalogPanel/CatalogPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ export function CatalogPanel() {
return (
<aside className={`catalog-panel${isCollapsed ? " catalog-panel--collapsed" : ""}`}>
<div className="catalog-panel-header">
{!isCollapsed && <span className="catalog-panel-title">Catalog: Default Library</span>}
{!isCollapsed && <span className="catalog-panel-title">Library: OpenUI Library</span>}
{isCollapsed && (
<span className="catalog-panel-icon-only" title="Catalog: Default Library">
<span className="catalog-panel-icon-only" title="Library: OpenUI Library">
<LayoutList size={16} />
</span>
)}
Expand Down
4 changes: 2 additions & 2 deletions docs/components/overview-components/overview-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function OverviewPage() {
>
A2UI
</a>{" "}
that reduces token usage by up to 52%. Define your component library with Zod schemas, get
that reduces token usage by up to 67.1%. Define your component library with Zod schemas, get
automatic system prompts, and parse LLM responses into renderable components.
</p>

Expand Down Expand Up @@ -244,7 +244,7 @@ import { FullScreen, openuiLibrary } from "@openuidev/react-ui";

<Separator className="my-8 sm:my-16" />

{/* Default Library Section */}
{/* OpenUI Library Section */}
<div id="library" className="mb-12 sm:mb-20">
<div className="mb-4 flex items-start gap-3 sm:mb-6 sm:gap-4">
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-fd-muted sm:size-12">
Expand Down
6 changes: 3 additions & 3 deletions docs/content/docs/openui-lang/benchmarks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Generated by GPT-5.2 at temperature 0. Token counts measured with `tiktoken` usi
| e-commerce-product | 2,449 | 2,381 | 1,166 | -52.4% | -51.0% |
| **TOTAL** | **10,180** | **9,948** | **4,800** | **-52.8%** | **-51.7%** |

OpenUI Lang uses roughly **half the tokens** of both JSON alternatives across all scenarios.
OpenUI Lang uses up to **67.1% fewer tokens** than both JSON alternatives across all scenarios.

---

Expand All @@ -92,7 +92,7 @@ Latency scales linearly with output token count at a given generation speed. At
| settings-panel | 20.73s | 20.08s | 9.00s | **2.3x faster** |
| e-commerce-product | 40.82s | 39.68s | 19.43s | **2.1x faster** |

The latency advantage compounds with UI complexity. A pricing page or dashboard — the kinds of UIs where Generative UI delivers the most value — render **2–3× faster** with OpenUI Lang.
The latency advantage compounds with UI complexity. A contact form renders **up to 3.0× faster**, and even complex dashboards and pricing pages — the kinds of UIs where Generative UI delivers the most value — render **2–3× faster** with OpenUI Lang.

---

Expand Down Expand Up @@ -134,7 +134,7 @@ The latency advantage compounds with UI complexity. A pricing page or dashboard

### Why is JSON-Render heavier than expected?

Vercel JSON-Render encodes each element as a separate `{"op":"add","path":"/elements/id","value":{...}}` line. The `op`, `path`, `value`, `type`, `props`, and `children` keys repeat for every node. For deeply nested UIs (dashboards, pricing pages), the structural repetition accumulates significantly — averaging **2.1× the tokens** of OpenUI Lang across our scenarios.
Vercel JSON-Render encodes each element as a separate `{"op":"add","path":"/elements/id","value":{...}}` line. The `op`, `path`, `value`, `type`, `props`, and `children` keys repeat for every node. For deeply nested UIs (dashboards, pricing pages), the structural repetition accumulates significantly — up to **3.0× the tokens** of OpenUI Lang across our scenarios.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/openui-lang/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ cancelBtn = Button("Cancel", "action:cancel_contact", "secondary")

<Cards>
<Card title="Quick Start" href="/docs/openui-lang/quickstart">
Start with the default library and render immediately.
Start with the OpenUI library and render immediately.
</Card>
<Card title="Defining Components" href="/docs/openui-lang/defining-components">
Build custom component contracts with Zod.
Expand Down
131 changes: 131 additions & 0 deletions docs/content/docs/openui-lang/interactivity.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: Interactivity
description: Handle actions, forms, and state in OpenUI components.
---

OpenUI components can be interactive. The `Renderer` manages form state automatically and exposes callbacks for actions and persistence.

## Actions

When a user clicks a button or follow-up, the component calls `triggerAction`. The `Renderer` wraps this into an `ActionEvent` and fires `onAction`.

```tsx
<Renderer
library={myLibrary}
response={content}
onAction={(event) => {
if (event.type === "continue_conversation") {
// event.humanFriendlyMessage — button label or follow-up text
// event.formState — field values at time of click
// event.formName — scoping form name, if any
}
}}
/>
```

### `ActionEvent`

| Field | Type | Description |
| :--------------------- | :------------------------------ | :--------------------------------------------- |
| `type` | `string` | Action type (see built-in types below). |
| `params` | `Record<string, any>` | Extra parameters from the component. |
| `humanFriendlyMessage` | `string` | Display label for the action. |
| `formState` | `Record<string, any> \| undefined` | Raw field state at time of action. |
| `formName` | `string \| undefined` | Form that scoped the action, if any. |

### Built-in action types

```ts
enum BuiltinActionType {
ContinueConversation = "continue_conversation",
OpenUrl = "open_url",
}
```

- `ContinueConversation` — sends the user's intent back to the LLM.
- `OpenUrl` — opens a URL in a new tab. Expects `params.url`.

### Using `triggerAction` in components

Inside `defineComponent`, use the `useTriggerAction` hook:

```tsx
const MyButton = defineComponent({
name: "MyButton",
description: "A clickable button.",
props: z.object({ label: z.string() }),
component: ({ props }) => {
const triggerAction = useTriggerAction();
return <button onClick={() => triggerAction(props.label)}>{props.label}</button>;
},
});
```

`triggerAction(userMessage, formName?, action?)` — the second and third arguments are optional.

---

## Form state

The `Renderer` tracks field values automatically. Components use `useSetFieldValue` and `useGetFieldValue` to read and write state.

### Persistence

Use `onStateUpdate` to persist field state (e.g. to a message in your thread store) and `initialState` to hydrate it on load.

```tsx
<Renderer
library={myLibrary}
response={content}
onStateUpdate={(state) => {
// state is a raw Record<string, any> of all field values
saveToBackend(state);
}}
initialState={loadedState}
/>
```

`onStateUpdate` fires on every field change. The state format is opaque — persist and hydrate it as-is.

### Field hooks

Use these inside `defineComponent` renderers:

| Hook | Signature | Description |
| :------------------ | :------------------------------------------------------------------------------------------------- | :--------------------------------- |
| `useGetFieldValue` | `(formName: string \| undefined, name: string) => any` | Read a field's current value. |
| `useSetFieldValue` | `(formName: string \| undefined, componentType: string \| undefined, name: string, value: any, shouldTriggerSaveCallback?: boolean) => void` | Write a field value. |
| `useFormName` | `() => string \| undefined` | Get the enclosing form's name. |
| `useSetDefaultValue`| `(options: { formName?, componentType, name, existingValue, defaultValue, shouldTriggerSaveCallback? }) => void` | Set a default if no value exists. |

---

## Validation

Form fields can declare validation rules. The `Form` component provides a validation context via `useFormValidation`.

```ts
interface FormValidationContextValue {
errors: Record<string, string | undefined>;
validateField: (name: string, value: unknown, rules: ParsedRule[]) => boolean;
registerField: (name: string, rules: ParsedRule[], getValue: () => unknown) => void;
unregisterField: (name: string) => void;
validateForm: () => boolean;
clearFieldError: (name: string) => void;
}
```

Built-in validators include `required`, `minLength`, `maxLength`, `min`, `max`, `pattern`, and `email`. Custom validators can be added via `builtInValidators`.

---

## Next Steps

<Cards>
<Card title="Renderer" href="/docs/openui-lang/renderer">
Full Renderer props reference.
</Card>
<Card title="API Reference" href="/docs/api-reference/lang-react">
Complete lang-react API.
</Card>
</Cards>
1 change: 1 addition & 0 deletions docs/content/docs/openui-lang/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"defining-components",
"system-prompts",
"renderer",
"interactivity",
"---Advanced---",
"specification",
"benchmarks"
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/openui-lang/quickstart.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Quick Start
description: Use the default library to render OpenUI Lang immediately.
description: Use the OpenUI library to render OpenUI Lang immediately.
---

This is the fastest path: use `openuiLibrary` from `@openuidev/react-ui` and render LLM output with `@openuidev/lang-react`.
Expand Down
4 changes: 2 additions & 2 deletions docs/content/docs/openui-lang/standard-library.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Using the Standard Library
description: Use the built-in default library from @openuidev/react-ui.
description: Use the built-in OpenUI library from @openuidev/react-ui.
---

OpenUI ships with a prebuilt `openuiLibrary` for common layouts, forms, content, and charts.
Expand All @@ -11,7 +11,7 @@ OpenUI ships with a prebuilt `openuiLibrary` for common layouts, forms, content,
npm install @openuidev/lang-react @openuidev/react-ui
```

## Render with default library
## Render with OpenUI library

```tsx
import "@openuidev/react-ui/components.css";
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/openui-lang/syntax.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ cancelBtn = Button("Cancel", "action:cancel", "secondary")
## Rules

- First statement is the entry point.
- For default library prompts, start with `root = Stack(...)`.
- For OpenUI library prompts, start with `root = Stack(...)`.
- Arguments are positional, based on Zod key order.
- Optional args can be omitted from the end.
- Forward references are allowed.
Expand Down
2 changes: 1 addition & 1 deletion docs/lib/layout.shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const gitConfig = {

export const siteConfig = {
githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
discordUrl: "https://discord.gg/ZeSTyHZTEV",
discordUrl: "https://discord.com/invite/Pbv5PsqUSv",
};

export function baseOptions(): BaseLayoutProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import "@openuidev/react-ui/styles/index.css";

import { openAIMessageFormat, openAIReadableStreamAdapter } from "@openuidev/react-headless";
import { FullScreen } from "@openuidev/react-ui";
import { defaultLibrary, defaultPromptOptions } from "@openuidev/react-ui/genui-lib";
import { openuiLibrary, openuiPromptOptions } from "@openuidev/react-ui/genui-lib";

const systemPrompt = defaultLibrary.prompt(defaultPromptOptions);
const systemPrompt = openuiLibrary.prompt(openuiPromptOptions);

export default function Home() {
return (
Expand All @@ -24,7 +24,7 @@ export default function Home() {
});
}}
streamProtocol={openAIReadableStreamAdapter()}
componentLibrary={defaultLibrary}
componentLibrary={openuiLibrary}
agentName="OpenUI Chat"
/>
</div>
Expand Down