# Catalogs A catalog is the consuming application's explicit allowlist of native A2UI capabilities. The package provides catalog contracts but no component catalog. ## Define Components Each component combines a Zod object schema with a presenter: ```tsx const Label = defineA2UIComponent({ schema: z.object({ text: A2UIDynamicStringSchema, tone: z.enum(["normal", "muted"]).optional(), }), presenter: ({ props }) => ( {props.text} ), description: "Native text label", }); const catalog = defineA2UICatalog({ id: "urn:example:a2ui:catalog:v1", components: { Label }, }); ``` The component name is the map key (`Label`). It must match the `component` field emitted in `updateComponents`. The surface catalog ID must match `catalog.id`. Descriptions are optional runtime metadata. If provided, they cannot be empty. The renderer does not automatically send descriptions or schemas to a model; the consumer or server owns any catalog IDL used for generation. ## Presenter Contract A presenter receives: - `props`: schema-validated, dynamically resolved values; - `surfaceId` and `componentId`: stable protocol identifiers; - `scope`: current template value and absolute pointer path; - `renderChild(componentId, scopePath?)`: renders an explicit child reference; - `dispatch(action)`: emits an event or invokes a registered local function. Presenters are ordinary React components. They may use React Native, Expo UI, a design system, or application-specific native modules. Keep them deterministic during render and perform effects through native callbacks. ## Dynamic Values And Bindings Use `A2UIDynamicStringSchema`, `A2UIDynamicNumberSchema`, `A2UIDynamicBooleanSchema`, `A2UIDynamicStringListSchema`, or `A2UIDynamicValueSchema` when a property may be a literal, binding, or function call. ```tsx const Input = defineA2UIComponent({ schema: z.object({ value: A2UIDynamicStringSchema }), presenter: ({ props }) => ( ), }); ``` If the wire value is `{ path: "/form/name" }`, the presenter receives the resolved string and a generated `setValue` setter. The setter updates that surface's data model at the same pointer. JSON Pointer escaping follows RFC 6901 (`~0` for `~`, `~1` for `/`). Relative paths resolve against the current template scope. This allows one template component to read and write each repeated item without losing its absolute pointer context. ## Children And Templates Use `A2UIChildListSchema` for structural children. Resolved children contain an ID and base path: ```tsx const Column = defineA2UIComponent({ schema: z.object({ children: A2UIChildListSchema }), presenter: ({ props, renderChild }) => ( {props.children.map((child) => renderChild(child.id, child.basePath))} ), }); ``` The wire form may be a static component-ID list or a structural template that repeats one component against a data-model path. Component graphs are checked for missing references, cycles, depth, and total count before commit. ## Actions Use `A2UIActionSchema` in interactive component schemas. Call `dispatch` only from an interaction handler: ```tsx const Button = defineA2UIComponent({ schema: z.object({ label: A2UIDynamicStringSchema, action: A2UIActionSchema, }), presenter: ({ props, dispatch }) => ( dispatch(props.action)}> {props.label} ), }); ``` Event context bindings resolve at dispatch time, so the consumer receives the latest form or template data. See [Streaming And Actions](./streaming-and-actions.md). ## Functions Functions are catalog-owned and schema validated: ```tsx const formatCount = defineA2UIFunction({ name: "formatCount", args: z.object({ count: z.number() }), returns: "string", execute: ({ count }) => `${count} items`, }); const catalog = defineA2UICatalog({ id: "urn:example:a2ui:catalog:v1", components: { Label }, functions: [formatCount], }); ``` Allowed return kinds are `string`, `number`, `boolean`, `object`, `array`, and `unknown`. Arguments and returned values are validated. Unknown functions, thrown implementations, and invalid results become `FUNCTION_ERROR`. Rendering functions should be deterministic and side-effect free because they may be evaluated again when data changes. A function-call action is local: it runs after presenter dispatch and is not forwarded through `onAction`. ## Catalog Versioning Treat a catalog ID as a contract version. If a component name or property changes incompatibly, publish a new catalog ID and keep old surfaces paired with their original catalog. Do not silently reinterpret an existing catalog ID.