OpenUI is a full-stack generative UI framework built around a compact, streaming-first language that lets models emit structured UI components without a training phase. The core idea: instead of forcing models to generate verbose JSON schemas or HTML, you define a component library, generate prompt instructions from it, and render UI as the model streams tokens. The project claims significantly better token efficiency than JSON for UI generation and includes optimized filtering for component access control.
This is not a wrapper around existing UI frameworks. OpenUI Lang is a purpose-built language for streaming component generation, paired with a React runtime and built-in libraries for charts, forms, tables, and layouts.
How OpenUI Lang Works
OpenUI Lang is a text-based language designed for streaming parsers. Models emit component declarations as they generate tokens, and the parser incrementally builds a UI tree without waiting for the full response.
Key design choices:
- Streaming-first syntax: The parser handles partial tokens and can backtrack when the model changes direction mid-component.
- Component-library-driven prompts: You define allowed components (with TypeScript types or runtime schemas), and OpenUI generates the prompt instructions the model needs.
- No training required: The model uses the component library as context. No fine-tuning, no parameter updates, no rebuild cycles.
- Filter at search time: You can restrict which components are available using an ID allowlist or slot bitmask, enforced before instantiation.
The language itself is compact. Instead of verbose JSON (which OpenUI avoids):
{
"type": "Card",
"props": {
"title": "Revenue",
"children": [
{
"type": "Chart",
"props": { "data": [10, 20, 30], "type": "bar" }
}
]
}
}
OpenUI Lang uses a terser syntax:
Card title="Revenue"
Chart data=[10,20,30] type="bar"
The parser reads this incrementally. If the model starts emitting Card, the parser knows to expect props and children. If the model backtracks or emits invalid syntax, the parser can recover without crashing the entire render.
Prompt Generation from Component Libraries
OpenUI generates prompt instructions by introspecting your component library. You define components with TypeScript types or runtime schemas, and OpenUI extracts:
- Component names
- Required and optional props
- Prop types (string, number, array, object)
- Allowed children (if any)
This metadata becomes part of the system prompt. The model sees a structured list of available components and their contracts, which reduces hallucination and keeps output aligned with what the runtime can render.
Example workflow:
- You define a
Chartcomponent with propsdata: number[],type: 'bar' | 'line', andtitle?: string. - OpenUI generates a prompt fragment: “You can use Chart with required props data (array of numbers) and type (bar or line), and optional prop title (string).”
- The model emits
Chart data=[10,20,30] type="bar" title="Sales". - The parser validates against the schema and instantiates the React component.
This is not runtime reflection in the traditional sense. OpenUI uses static analysis (for TypeScript) or runtime introspection (for JavaScript) to build the prompt at initialization time, not per-request.
Streaming Parser and Backtracking
The streaming parser is the critical piece. It processes tokens as they arrive, builds a partial component tree, and handles incomplete or malformed input.
Backtracking mechanism:
- The parser maintains a stack of open components.
- If the model emits a closing tag or starts a new component before finishing the current one, the parser pops the stack and adjusts the tree.
- If the model emits invalid syntax (unknown component, missing required prop), the parser can either skip the token, insert a placeholder, or halt rendering.
This is not a traditional AST parser. It’s designed for incremental, error-tolerant parsing where the input stream is unreliable.
Failure modes:
- Model hallucinates a component: Parser skips it or renders a fallback.
- Model emits partial prop value: Parser waits for more tokens or uses a default.
- Model closes a component early: Parser finalizes the subtree and continues.
The parser does not retry or ask the model to regenerate. It works with what it gets.
Component Filtering and Access Control
OpenUI includes optimized filtering routines for controlling component access at search time. This is not post-render filtering. It happens before component instantiation.
Two filtering modes:
- ID allowlist: You provide a list of allowed component IDs. The filter scans the component library and marks disallowed components as unavailable.
- Slot bitmask: You provide a bitmask where each bit represents a component slot. The filter checks bits to determine availability.
The filtering process handles large component libraries efficiently by processing multiple component IDs in parallel operations.
Why filter at search time?
- Security: Prevent the model from emitting dangerous components (e.g.,
FileUpload,AdminPanel). - Context control: Limit the component set based on user role, session state, or workflow stage.
- Token efficiency: Smaller prompt when you exclude irrelevant components.
Filtering happens before prompt generation. If a component is filtered out, it never appears in the system prompt.
Built-In Component Libraries
OpenUI ships with component libraries for common UI patterns:
| Library | Components | Use Case |
|---|---|---|
| Charts | Bar, Line, Pie, Scatter | Data visualization |
| Forms | Input, Select, Checkbox, Radio | User input |
| Tables | Table, Row, Cell, Header | Tabular data |
| Layouts | Grid, Flex, Stack, Card | Spatial arrangement |
| Feedback | Alert, Toast, Modal, Spinner | User notifications |
Each library is a TypeScript module with React components and OpenUI Lang schemas. You can use them as-is or extend them with custom components.
Custom components:
You define a React component and a schema:
export const PriceCard = ({ price, currency }: { price: number; currency: string }) => (
<div className="price-card">
{currency}{price}
</div>
);
export const priceCardSchema = {
name: 'PriceCard',
props: {
price: { type: 'number', required: true },
currency: { type: 'string', required: true }
}
};
OpenUI reads the schema, generates the prompt fragment, and registers the component with the runtime.
Deployment Shape
OpenUI is a full-stack framework. You run it as:
- Server-side: Node.js or edge runtime (Vercel, Cloudflare Workers).
- Client-side: React app with streaming SSE or WebSocket connection to the model.
Typical architecture:
- Client sends a user query to the server.
- Server calls the LLM with the OpenUI-generated prompt.
- LLM streams OpenUI Lang tokens.
- Server parses tokens incrementally and sends React component trees to the client via SSE.
- Client renders components as they arrive.
State management:
OpenUI does not include a built-in state system. You manage state with React hooks, Zustand, or your own store. The framework only handles the streaming parse and render cycle.
Cost and rate limiting:
OpenUI does not include built-in rate limiting or token budgeting for LLM calls. You need to implement these controls at the API layer or use your LLM provider’s rate limiting features.
Observability:
You need to instrument the parser and renderer yourself. Key metrics:
- Tokens per component (to track efficiency)
- Parse errors per request
- Backtrack count (to detect model instability)
- Component instantiation time
OpenUI does not ship with telemetry hooks. You add them in the parser callbacks.
Security Boundaries
OpenUI is not a security boundary. Treat it as a rendering layer, not a trust boundary. If the model emits a component, it runs in the same React context as the rest of your app.
Risks:
- Component injection: Model emits a component that calls an API or mutates state.
- Prop injection: Model emits props that trigger XSS or code execution.
- Resource exhaustion: Model emits a deeply nested tree that crashes the renderer.
Mitigations:
- Use the ID allowlist to restrict components.
- Validate props at the schema level (OpenUI does this automatically).
- Set a max depth for the component tree.
- Run the parser in a worker thread or separate process.
The filtering system helps with component injection, but it does not prevent prop-level attacks. You need to sanitize prop values yourself.
Token Efficiency
OpenUI claims significantly better token efficiency than JSON (the project documentation cites 67% improvement based on syntax compactness). This is based on:
- Shorter syntax (no
"type":,"props":,"children":boilerplate). - No closing braces or brackets.
- Implicit prop types (the parser infers types from the schema).
Example comparison:
JSON (82 tokens):
{"type":"Card","props":{"title":"Revenue"},"children":[{"type":"Chart","props":{"data":[10,20,30],"type":"bar"}}]}
OpenUI Lang (27 tokens):
Card title="Revenue"
Chart data=[10,20,30] type="bar"
The efficiency gain assumes typical UI structures with nested components. For flat UIs, the savings are smaller. See the OpenUI documentation for detailed benchmarks.
Failure Modes
Model emits invalid syntax:
The parser skips the token or renders a fallback. You see a placeholder in the UI.
Model hallucinates a component:
If the component is not in the library, the parser ignores it. If it is in the library but filtered out, the parser skips it.
Model emits partial response:
The parser renders what it has. The UI is incomplete but functional.
Parser crashes:
If the parser hits an unrecoverable error (e.g., stack overflow from deeply nested components), the entire render fails. You need to catch this at the server level and return an error to the client.
Technical Verdict
Use OpenUI when:
- You need to generate structured UI from LLM output without training a model.
- Token efficiency matters (you are paying per token or hitting rate limits).
- You want to filter components dynamically based on user context or security policy.
- You are building a chat interface or agentic UI that needs to render components as the model streams.
Avoid OpenUI when:
- You need a sandboxed execution environment (OpenUI does not isolate components).
- You require built-in state management, rate limiting, or observability (you will need to add these yourself).
- Your UI is simple enough that plain text or Markdown is sufficient.
- You need to support models that cannot follow the OpenUI Lang syntax (you will need to fine-tune or use a different approach).
OpenUI is a plumbing layer for streaming generative UI. It handles the parse and render cycle but leaves state, security, cost control, and observability to you.
Source Links
- Primary source: OpenUI on GitHub
- Playground: OpenUI Playground
- Documentation: OpenUI Docs