Generate Lynx OpenUI Programs
Generate valid OpenUI Lang v0.5 consumed by
<OpenUiRenderer response={...}>. Produce declarative DSL, not application
code.
Read The References
- Always read components.md before generating a program. Treat caller-supplied component signatures as authoritative extensions or replacements.
- Read runtime.md when the request involves state, tools, live data, mutations, actions, repeated query data, or edits.
- Read examples.md for a non-trivial layout or tool workflow. Adapt patterns; do not copy irrelevant content.
Workflow
- Identify the requested UI, exact active component catalog, configured root
component, available tool schemas, host-supplied prefetched Query keys,
media URLs, and whether the host supports full programs or
mergeStatementspatches. - Choose the data mode:
- Use static values for self-contained content and when no tool exists.
- Use
Queryonly for an actual read tool supplied by the caller or host. - Use
Mutationonly for an actual write tool and trigger it from an explicitAction.
- Select only components and positional arguments allowed by the active
catalog. Treat an explicitly reduced catalog as the complete vocabulary:
built-ins omitted from it are unavailable. Use the host's explicit root
override when present; otherwise use the Library's default
Stackroot. - Write a streaming-friendly graph. For a complete program, put the root first, followed by state, queries or mutations, structural components, and leaf content. For an edit-mode patch, return only changed statements.
- Validate syntax, references, reachability, component names, argument order, tool names, and action targets before returning the program.
Output Contract
- Return only OpenUI Lang. Do not return Markdown, code fences, prose, JSON, XML, HTML, JavaScript, TypeScript, JSX, or CSS.
- Put one assignment statement on each line.
- For a complete program, make the first non-empty line assign
rootto a call of the host's explicit root override, for exampleroot = Shell(...). Otherwise use the Library's defaultroot = Stack(...). - Return a complete program by default. When the caller explicitly says the
host uses edit mode or
mergeStatements, return only changed statements and includerootonly when the root graph changes. - If a request needs an unavailable component or capability, render a concise explanation with supported OpenUI components instead of inventing syntax.
Syntax Rules
- Use
identifier = Expressionfor ordinary declarations and$identifier = defaultValuefor mutable state. - Pass component arguments positionally. Never use named arguments such as
gap: "m". - Omit optional arguments only from the end. When setting a later argument, provide valid values for every earlier position.
- Use double-quoted strings and escape embedded quotes and backslashes.
- Use references or inline components. Prefer references for the root and major sections so streaming reveals the structure progressively.
- Ensure every referenced identifier is defined. Ensure every declaration
other than
rootis reachable fromroot, an expression reachable fromroot, or an action reachable fromroot. - Use only documented operators, built-ins, components, and action steps.
Correct positional layout using the default Stack root:
root = Stack([header, content], "column", false, "l", "stretch", "start")
Incorrect positional layout:
root = Stack([header, content], "column", "l", "stretch")
The incorrect form passes a string into wrap and shifts every later prop.
Data And Interaction Rules
- Do not invent tool names. If no tool schema is supplied, use static data or an explanatory supported UI.
- Give every
Querya representative default result matching the real tool shape so the UI renders in the first synchronous render after the response stabilizes and before the request resolves. - When the host supplies prefetched Query results, preserve the corresponding Query assignment names. They are statement IDs and prefetch keys, not tool names. See runtime.md.
- Keep
QueryandMutationdeclarations on ordinary identifiers, never$identifiers. - Use
@Eachfor query-backed repeated UI. Keep its loop-dependent component inline inside@Each. - Trigger write operations only from an explicit submit or confirmation action
on an active-catalog component. With the default catalog, use
Button. Do not attach a mutation to an input's change action. - When an input must update a
$variable, use the same-keynamepattern in runtime.md. Do not invent an event-value variable. - Execute multi-step actions in deliberate order. Remember that a failed mutation stops the remaining steps.
- Use
@ToAssistantfor conversational continuation and@OpenUrlonly with a caller-provided or trustworthy URL. - Use caller-provided media URLs. Do not fabricate CDN URLs or assume the host resolves image-search strings.
Lynx UI Rules
- Prefer compact, mobile-first layouts with a shallow component tree.
- Apply component-specific preferences only when those components exist in the active catalog; otherwise follow the supplied custom signatures.
- With the default catalog, use
StackorColumnfor ordinary vertical structure,Rowfor small horizontal groups, andListfor grouped or repeated content. - When
Imageexists, prefer explicit variants so the Lynx renderer has concrete sizing. - With the default catalog, keep readable text in
TextorTextContent; do not emit raw Lynx elements. - When available, use
Tabsfor alternate views andModalfor tap-to-open details. Pass a modal trigger first and content second; do not also render the trigger as a sibling.
Final Verification
Before returning, verify all of the following:
- A complete program assigns
rootto the host's explicit root override when present, or to the Library's defaultStackroot otherwise. An edit-mode patch omitsrootunless the root graph changes. The response contains only OpenUI Lang. - Every component exists in the active catalog and every argument matches its positional schema. A reduced catalog contains no implicit built-ins, and the root call uses its configured root component.
- No
Form,Input,Select,SelectItem,FormControl, table, or chart is used unless the caller supplied that custom component. - Every reference resolves and no declaration is orphaned.
- Every
QueryorMutationuses a real supplied tool and is referenced by visible UI or a visible action. - Every intended prefetched Query uses its exact host-supplied key as the assignment name, and every Query still has a shape-correct default.
- Every
$variableparticipates in visible UI, a query argument, or an action. - Every
@Eachloop variable stays inside its inline template expression. - No working dynamic list relies on
TemplateChildrenin the built-in Lynx renderer. - If a parser is available, parsing finishes with a root, no validation errors, no unresolved references, and no orphaned statements.