@openuidev/react-lang
API reference for the OpenUI Lang runtime, library, parser, and renderer.
Use this package for OpenUI Lang authoring and rendering.
Import
import {
defineComponent,
createLibrary,
Renderer,
BuiltinActionType,
createParser,
createStreamingParser,
} from "@openuidev/react-lang";defineComponent(config)
Defines a single component with name, Zod schema, description, and React renderer. Returns a DefinedComponent with a .ref for cross-referencing in parent schemas.
function defineComponent<T extends z.ZodObject<any>>(config: {
name: string;
props: T;
description: string;
component: ComponentRenderer<z.infer<T>>;
}): DefinedComponent<T>;interface DefinedComponent<T extends z.ZodObject<any> = z.ZodObject<any>> {
name: string;
props: T;
description: string;
component: ComponentRenderer<z.infer<T>>;
/** Use in parent schemas: `z.array(ChildComponent.ref)` */
ref: z.ZodType<SubComponentOf<z.infer<T>>>;
}createLibrary(input)
Creates a Library from an array of defined components.
function createLibrary(input: LibraryDefinition): Library;Core types:
interface LibraryDefinition {
components: DefinedComponent[];
componentGroups?: ComponentGroup[];
root?: string;
}
interface ComponentGroup {
name: string;
components: string[];
notes?: string[];
}
interface Library {
readonly components: Record<string, DefinedComponent>;
readonly componentGroups: ComponentGroup[] | undefined;
readonly root: string | undefined;
prompt(options?: PromptOptions): string;
toJSONSchema(): object;
toSpec(): PromptSpec;
}
interface PromptOptions {
preamble?: string;
additionalRules?: string[];
examples?: string[];
toolExamples?: string[];
editMode?: boolean;
inlineMode?: boolean;
/** Enable Query(), Mutation(), @Run, built-in functions. Default: true if tools provided. */
toolCalls?: boolean;
/** Enable $variables, @Set, @Reset, built-in functions. Default: true if toolCalls. */
bindings?: boolean;
}<Renderer />
Parses OpenUI Lang text and renders nodes with your Library.
interface RendererProps {
response: string | null;
library: Library;
isStreaming?: boolean;
onAction?: (event: ActionEvent) => void;
onStateUpdate?: (state: Record<string, unknown>) => void;
initialState?: Record<string, any>;
onParseResult?: (result: ParseResult | null) => void;
toolProvider?:
| Record<string, (args: Record<string, unknown>) => Promise<unknown>>
| McpClientLike
| null;
queryLoader?: React.ReactNode;
onError?: (errors: OpenUIError[]) => void;
}Tool Provider
Handles Query() and Mutation() tool calls at runtime. The toolProvider prop accepts two forms:
- Function map —
Record<string, (args) => Promise<unknown>>— the simplest option - MCP client — any object implementing
callTool({ name, arguments })(e.g. from@modelcontextprotocol/sdk)
The Renderer detects which form was passed and normalizes internally.
Error types
type OpenUIErrorSource = "parser" | "runtime" | "query" | "mutation";
interface OpenUIError {
source: OpenUIErrorSource;
code: string;
message: string;
statementId?: string;
component?: string;
path?: string;
hint?: string;
}
class ToolNotFoundError extends Error {
toolName: string;
availableTools: string[];
}Error codes: unknown-component, missing-required, null-required, inline-reserved, tool-not-found, parse-failed, parse-exception, runtime-error, render-error.
Actions
enum BuiltinActionType {
ContinueConversation = "continue_conversation",
OpenUrl = "open_url",
}
interface ActionEvent {
type: string;
params: Record<string, any>;
humanFriendlyMessage: string;
formState?: Record<string, any>;
formName?: string;
}Action steps (runtime types from the evaluator):
type ActionStep =
| { type: "run"; statementId: string; refType: "query" | "mutation" }
| { type: "continue_conversation"; message: string; context?: string }
| { type: "open_url"; url: string }
| { type: "set"; target: string; valueAST: ASTNode }
| { type: "reset"; targets: string[] };| Step type | Triggered by | Description |
|---|---|---|
"run" | @Run(ref) | Execute a Mutation or re-fetch a Query. refType indicates which. |
"set" | @Set($var, val) | Change a $variable. valueAST is evaluated at click time. |
"reset" | @Reset($a, $b) | Restore $variables to declared defaults. |
"continue_conversation" | @ToAssistant("msg") | Send message to LLM. Optional context. |
"open_url" | @OpenUrl("url") | Open URL in new tab. |
Parser APIs
Both createParser and createStreamingParser accept a LibraryJSONSchema (from library.toJSONSchema()).
interface LibraryJSONSchema {
$defs?: Record<
string,
{
properties?: Record<string, unknown>;
required?: string[];
}
>;
}
function createParser(schema: LibraryJSONSchema): Parser;
function createStreamingParser(schema: LibraryJSONSchema): StreamParser;
interface Parser {
parse(input: string): ParseResult;
}
interface StreamParser {
push(chunk: string): ParseResult;
getResult(): ParseResult;
}Core parsed types:
interface ElementNode {
type: "element";
typeName: string;
props: Record<string, unknown>;
partial: boolean;
}
/**
* Parser-level validation errors (schema mismatches).
*/
type ValidationErrorCode =
| "missing-required"
| "null-required"
| "unknown-component"
| "inline-reserved";
interface ValidationError {
code: ValidationErrorCode;
component: string;
path: string;
message: string;
statementId?: string;
}
interface ParseResult {
root: ElementNode | null;
meta: {
incomplete: boolean;
/** References used but not yet defined (dropped as null in output). */
unresolved: string[];
/** Value statements defined but not reachable from root. Excludes $state, Query, and Mutation. */
orphaned: string[];
statementCount: number;
/**
* Validation errors:
* - "missing-required" — required prop not provided
* - "null-required" — required prop explicitly null
* - "unknown-component" — component not in library schema
* - "inline-reserved" — Query/Mutation used inline instead of top-level
* - "excess-args" — more positional args than schema params (extras dropped, component still renders)
*/
errors: ValidationError[];
};
/** Extracted Query() statements with tool name, args AST, defaults AST */
queryStatements: QueryStatementInfo[];
/** Extracted Mutation() statements with tool name, args AST */
mutationStatements: MutationStatementInfo[];
/** Declared $variables with their default values */
stateDeclarations: Record<string, unknown>;
}Context hooks (inside renderer components)
// Reactive state binding — preferred for form inputs and $variable-bound components
function useStateField(
name: string,
value?: unknown,
): {
value: unknown;
setValue: (value: unknown) => void;
};
function useRenderNode(): (value: unknown) => React.ReactNode;
function useTriggerAction(): (
userMessage: string,
formName?: string,
action?: { type?: string; params?: Record<string, any> },
) => void;
function useIsStreaming(): boolean;
function useGetFieldValue(): (formName: string | undefined, name: string) => any;
function useSetFieldValue(): (
formName: string | undefined,
componentType: string | undefined,
name: string,
value: any,
shouldTriggerSaveCallback?: boolean,
) => void;
function useFormName(): string | undefined;
function useSetDefaultValue(options: {
formName?: string;
componentType: string;
name: string;
existingValue: any;
defaultValue: any;
shouldTriggerSaveCallback?: boolean;
}): void;Form validation APIs
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;
}
function useFormValidation(): FormValidationContextValue | null;
function useCreateFormValidation(): FormValidationContextValue;
function validate(
value: unknown,
rules: ParsedRule[],
customValidators?: Record<string, ValidatorFn>,
): string | undefined;
function parseRules(rules: unknown): ParsedRule[];
function parseStructuredRules(rules: unknown): ParsedRule[];
const builtInValidators: Record<string, ValidatorFn>;Context providers for advanced usage:
const FormValidationContext: React.Context<FormValidationContextValue | null>;
const FormNameContext: React.Context<string | undefined>;