Catalogs, built-ins, and custom components
A catalog is the contract between your Agent and your client: it lists the components the renderer is allowed to instantiate and the functions it can execute, and it carries the optional JSON schemas an Agent reads during a handshake. This guide covers everything about composing that contract — from the one-line minimum, through the built-in component set, to shipping your own components and generating their schemas.
If you only skim one section, make it The built-in components and Basic-catalog functions: those are the vocabulary your Agent gets to work with.
What a catalog is
A catalog has two kinds of entries:
- Components — map a protocol name (
"Text","Card", your"MyChart") to a ReactLynx component the client renders. - Functions — map a protocol function name (
"formatDate","required") to a client-side implementation the renderer calls while resolving props, actions, and validation checks.
You build one with defineCatalog, then either pass it (or a raw input
array) to <A2UI catalogs={…}> for rendering, or run it through
serializeCatalog to announce the contract to your Agent.
A component's protocol name comes from displayName ?? component.name,
unless you pair it with a manifest — in which case the manifest's top-level
key is authoritative. The name is the only thing the Agent references, so
keeping it stable matters (see the minifier warning below).
Start small: renderer-only components
If your app only needs to render, pass bare components. No schemas, no ceremony.
You can pass the same array straight to <A2UI> without calling
defineCatalog yourself — the component composes it internally:
Bundlers tree-shake unused components: pulling in Text does not drag
Button, Card, or any other built-in into your bundle.
⚠️ Production minifiers rename function declarations, which breaks the
component.namefallback. For production safety, either set an explicitdisplayNameon every custom component (the string literal survives minification) or pair the component with itscatalog.jsonmanifest using the tuple form — the manifest key is authoritative and immune to minification.
The built-in components
The package ships 20 A2UI v0.9 basic-catalog renderers. Each is an
independent, tree-shakeable export, available from the root or from
@lynx-js/genui/a2ui/catalog/<Name>.
Layout and containers
Content
Input and actions
Data visualization
Feedback
To learn the exact props each one accepts, read its manifest at
@lynx-js/genui/a2ui/catalog/<Name>/catalog.json — that JSON is the same
schema the Agent sees.
Adding manifests for Agent handshakes
If you want serializeCatalog(...) to emit JSON Schema for each component
(so the Agent knows which props to send), pair each component with the JSON
generated at dist/catalog/<Name>/catalog.json using the tuple form:
The protocol name lives in the JSON as the top-level key, so the runtime
never duplicates it. Components you register without a manifest still
render fine — they just serialize to { name } only, which tells the Agent
the component exists without describing its props.
Basic-catalog functions
A2UI messages can embed function calls in dynamic props, action payloads,
and validation checks — for example { call: 'formatDate', args: { … } }.
These run on the client at render time. To make them available, spread
...basicFunctions into the same catalog input list:
basicFunctions is an array of ready-made entries whose implementations
come straight from the upstream @a2ui/web_core basic catalog, so the wire
contract stays aligned with the A2UI v0.9 spec for free. It covers 25
functions:
Note the mixed casing — comparison/text helpers use
snake_case(not_equals,starts_with) while formatters usecamelCase(formatDate,openUrl). These are the upstream A2UI v0.9 names; use them verbatim in messages.
Include ...basicFunctions whenever your Agent might emit any of these. If
a message references a function the catalog does not contain, that call
resolves to undefined rather than throwing.
If you build your own renderer instead of using <A2UI>, call
registerBasicFunctions() once to register the same implementations into
the shared functionRegistry.
Why there is no catalog/all
The package intentionally does not ship an "all-in-one" catalog
constant or a @lynx-js/genui/a2ui/catalog/all export. A single top-level
array referencing every built-in would defeat tree-shaking — every consumer
of that aggregate would bundle every component, even the ones they never
render. Composition is per-component, and the bundle cost stays visible at
the import site.
The paste-able "every built-in" recipe
When you genuinely want all of them, keep the list at the integration site. This composes every built-in component with its manifest, plus the basic functions:
Drop the manifest import and tuple form for any component whose schema you
do not need to send to the Agent — defineCatalog([Text, Button]) is
perfectly valid. Keep ...basicFunctions if your messages use function
calls.
Custom components
A catalog component is anything that takes a single props object and
returns a ReactNode. Its function name — or its displayName — is the
protocol name the Agent will use:
Custom components receive runtime-shaped props from the protocol stream. For
anything beyond a leaf component, reach into @lynx-js/genui/a2ui/react,
the contract custom components plug into:
useDataBinding— resolve a bound value ({ path }) against the surface's data model and get a setter back, so inputs can write user edits into the model.useResolvedProps— resolve a whole prop bag at once, expanding data bindings and function calls into concrete values.useAction— turn a protocol action into asendActioncallback you fire on tap/submit; the result loops back out through<A2UI onAction>.useChecks— evaluate validation checks (built from basic functions likerequired/email) and report pass/fail with messages.NodeRenderer— render child component ids against the same surface, the same way the built-ins render their children.
Generating a manifest for a custom component
If the Agent needs to know a custom component's props, generate a manifest and pair it the same way you would a built-in.
-
Describe the props as a TypeScript
interfaceand annotate it with the@a2uiCatalog <ComponentName>JSDoc tag so the extractor picks it up. -
Run the public CLI to emit the JSON:
-
Pair the generated JSON with the component:
npx @lynx-js/genui a2ui generate catalog is the user-facing command;
@lynx-js/genui/a2ui-catalog-extractor is the TypeDoc-powered engine behind
it. Two constraints to keep extraction happy: the component folder name must
match the exported function name (src/catalog/MyChart/index.tsx exports
function MyChart), and framework-level props are excluded from the emitted
schema. See the
extractor README for details.
Catalog API reference
All of these are exported from @lynx-js/genui/a2ui (and from the
/catalog subpath).
defineCatalog(inputs)— builds the runtime catalog.inputsis an array that can mix bare components,[component, manifest]tuples, already-resolved entries (e.g. frommergeCatalogs), and function entries. Duplicate names within the same kind are rejected. Function entries register their impls intofunctionRegistryimmediately, so anyexecuteFunctionCallafterdefineCatalogcan route to them.mergeCatalogs(...catalogs)— merges catalogs with last-write-wins on duplicate names. Useful for layering: a page catalog overrides a brand catalog which overrides the built-ins.serializeCatalog(catalog)— emits the JSON manifest for the Agent handshake. Components without an attached schema serialize to{ name }only; functions serialize with their parameter schema when available.resolveCatalog(catalog)— returns aname → componentmap. The renderer uses it internally to resolve{ component: 'Text' }; exposed for advanced cases.defineFunction(impl, manifest?)— wraps a function implementation into a catalog entry. With a manifest, the name comes from the manifest's key and the schema is announced to the Agent; without one, the name comes fromimpl.displayName ?? impl.nameand the Agent simply won't see the parameter schema.
Where to go next
- Overview and architecture — how a message becomes UI, the responsibility split, and the export map.
- System prompts — generate the model instructions that pair an Agent with your catalog.
- Open the A2UI playground — try it live.

