Libraries, built-ins, and custom components
An OpenUI Library is the contract between your Agent and your client. It describes the component calls the Agent may write, provides JSON Schema for parsing and prompt generation, and maps each allowed name to a trusted ReactLynx renderer.
This guide covers the default Library, its 26 built-in components, and the process for adding or replacing components.
What a Library contains
Each component definition has four parts:
- a stable OpenUI component
name; - a Zod object schema whose field order defines positional argument order;
- a short
descriptionused in generated prompts; - a trusted ReactLynx
componentimplementation.
createOpenUiLibrary() assembles those definitions into a Library with:
Create the default Library once and keep its identity stable:
Built-in components
By default, createOpenUiLibrary() includes the following components. Their
definitions are exported from the aggregate @lynx-js/genui/openui/catalog
entry and from individual subpaths such as
@lynx-js/genui/openui/catalog/Stack.
Layout
Content
Buttons
Data display and media
Overlays
Inputs
Button, CheckBox, Modal, RadioGroup, Slider, and TextField use
@lynx-js/lynx-ui; add that peer dependency when those components are
available to generated UI.
The default visual layer is Luna: import
@lynx-js/luna-styles/index.css before
@lynx-js/genui/openui/styles/theme.css, then place the renderer under
luna-light, luna-dark, lunaris-light, or lunaris-dark. lynx-ui is
headless, so Luna supplies tokens rather than replacement component classes.
For exact current positional signatures and a live preview, open the OpenUI Catalog. The schema is the source of truth: optional arguments may be omitted only from the right, and named-argument syntax is not supported.
OpenUI component syntax
The order of fields in a component's Zod object is its wire-level positional
argument order. For example, the built-in Stack schema begins with
children, direction, wrap, gap, align, and justify, so this is valid:
This is not valid OpenUI Lang:
Child components are values. They can be declared as named statements or used inline when the parent schema accepts them:
Add a custom component
Install Zod if the application does not already depend on it:
Define a component with a stable name, ordered prop schema, prompt description,
and ReactLynx renderer. Put styles in a CSS class rather than inline style
objects, and wrap visible text in <text>.
The Agent can now emit:
Caller-provided components and groups are appended after the defaults. If a
custom component uses the same name as a built-in, the later custom definition
wins in the components map. Treat that as an intentional override and keep
the prompt-side schema identical.
Choose components explicitly
Set includeDefaultComponents to false when the Library should contain only
caller-provided definitions. Import each built-in that remains part of the
Library from the catalog and pass it through components:
Disabling the defaults omits both the 26 built-in component definitions and
their groups. The root still defaults to Stack, and Library creation fails if
the selected components do not contain the root. Import and pass Stack, as in
the example, or set root to another caller-provided component. Keep the Agent
prompt and client Library restricted to the same component names.
includeDefaultComponents controls the Library vocabulary. When the factory is
imported from the main openui entry, that entry still statically references the
default catalog. Use openui/explicit and individual component subpaths, as
above, when unselected catalog modules must stay outside the static dependency
graph.
Render nested component values
If a custom prop accepts child components, use renderNode from the component
render contract. It recursively renders elements, arrays, and primitive values
using the active Library.
Do not call generated functions or evaluate generated strings inside a custom component. Let the OpenUI parser/runtime resolve expressions before props reach the renderer.
Runtime hooks for interactive components
Custom components may use the hooks exported from
@lynx-js/genui/openui:
All of these hooks must run below <OpenUiRenderer>. Interactive components
should honor isStreaming so a user cannot act on a partial model response.
JSON Schema and parser utilities
Use the Library schema for parsing outside the renderer or for inspection:
For streamed text, use createStreamingParser. Its set(fullText) method is
convenient when your UI stores the accumulated response; push(chunk) accepts
only the new delta.
Use the renderer's onParseResult callback when you already render the same
response. That avoids creating a second parser just to inspect root,
stateDeclarations, data statements, or meta diagnostics.
Keep the Agent contract aligned
Adding a component only to the ReactLynx Library is not enough: the Agent must
receive the same name, positional schema, and description. The default prompt
entry is deliberately headless so server code does not import ReactLynx or
component CSS. For custom components, define matching headless prompt entries
and pass them to buildOpenUiSystemPrompt.
See System prompts for the complete CLI and programmatic flow.

