Properties declare what configurable inputs a component accepts. The same schema drives four things at once: type-safeDocumentation Index
Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
Use this file to discover all available pages before exploring further.
props in the body, runtime validation, form-control generation in editors that integrate BindJS, and JSON Schema generation for downstream consumers — validators, MCP tool input checks, and LLMs reasoning about how to invoke a component. You write the schema once; everything else is derived.
How properties work
The lifecycle is the same in every BindJS runtime:- Definition — a component declares a
propertiesschema in itsdefineComponentcall. Each entry is built from a property helper such asPropertyStringorPropertyEnum. - Runtime resolution — at render time the runtime resolves the schema, applies defaults, and produces a typed props object that the body function receives.
- Editor integration — editors integrating BindJS read the schema to render input controls; updates flow back through the runtime as new prop values.
- Re-render — when props change, the body re-runs with the new values.
props argument is inferred from the schema — there’s no manual ComponentProps interface to maintain. Change a helper and the body’s prop type changes with it.
Property schema structure
A component’sproperties field is a record from property name to a property-helper call. The helpers compose: array helpers nest a valueType, group helpers nest a properties block, and component helpers can declare an allowedComponents allowlist for slots.
properties field also accepts a function returning a record, for cases where the schema needs runtime context — for example, looking up enum options from the host.
Property helpers
Eleven helpers cover the input types BindJS supports: scalars, enums, dates, arrays, groups, host-resolved references (assets and content), and component slots. Every helper accepts the base fields (title, description, required, defaultValue, examples, inspector, validation) plus its own helper-specific options.
PropertyString
For text input — single-line, multi-line, code, or markdown.inspector.control: "multiline" and optionally numberOfLines. To enable a markdown formatting toolbar, set inspector.markdown: true:
inspector.control: "code".
The text input variant. Defaults to
"singleline".Placeholder text shown when the field is empty.
Show a markdown formatting toolbar on multi-line fields.
The visible line count for multi-line fields.
Minimum character length.
Maximum character length.
A regular expression the value must match.
A format constraint applied alongside the pattern check.
PropertyNumber
For numeric input accepting any real number, with aninput field or slider control.
The control variant.
"slider" renders a draggable range; "input" renders a numeric field. Defaults to "input".The step increment for the input or slider.
A placeholder value shown when the field is empty.
Minimum allowed value.
Maximum allowed value.
PropertyInteger
The same shape asPropertyNumber, but restricted to integer values. The generated JSON Schema emits { type: "integer" } — useful when downstream consumers need an integer guarantee at the schema level (for example, an OpenAPI path parameter typed as integer).
PropertyNumber when any real number is acceptable; use PropertyInteger when the consumer needs an integer guarantee at the schema level.
PropertyBoolean
For toggle and switch controls.PropertyBoolean has no helper-specific options beyond the base fields.
PropertyEnum
For selection from a predefined set of options. Options can be plain strings, plain numbers, objects withvalue plus label (for text labels), or objects with value plus icon (for icon-only segmented controls).
Plain-string options:
The selectable values. Use plain strings/numbers for compact lists,
{ value, label } for labeled options, or { value, icon } for icon-only segmented controls.The control variant.
"segmented" renders inline buttons; "dropdown" renders a select menu. Defaults to "dropdown".PropertyDate
For date selection, with an ISO 8601 string at runtime.Placeholder text for the date picker.
The minimum allowed date as an ISO 8601 string.
The maximum allowed date as an ISO 8601 string.
PropertyArray
For repeating lists of values. The item type is set byvalueType — any other property helper.
Array of strings:
The item type. Any property helper is allowed, including nested
PropertyArray, PropertyGroup, and PropertyComponent.Minimum number of items.
Maximum number of items.
PropertyAsset
For media file selection. The host’s asset system resolves the value to an object with exactly one ofimage, video, audio, or model, each carrying url, dimensions, mimeType, and a host-supplied id.
The allowed asset types. Common values:
"image", "video", "audio", "model". The host defines the full set.PropertyContent
For referencing another content item. The host resolves the reference through its content system.PropertyContent has no helper-specific options beyond the base fields. The shape of the resolved value is host-defined.
PropertyComponent
For embedding a child component. Combine withallowedComponents to constrain the slot to a specific set of component types, and with environment to pass context values down to the child.
PropertyComponent is the foundation for component slots. Wrapping it in PropertyArray produces a list slot — see Slots of child components for the patterns.
Component names allowed in this slot. The host’s component registry resolves names; LLM and editor consumers use this list to narrow the candidate set.
Environment values to inject into the child’s render context. Children read these via
useEnvironment.PropertyGroup
For grouping related properties under a single collapsible section. The group’s ownproperties field follows the same record-of-helpers structure as a component’s top-level schema.
properties may include other groups, arrays of groups, or any other helper.
The nested fields. Inferred as a nested object on the body’s
props.Slots of child components
Layout components declare where children can be placed by combiningPropertyComponent (a single slot) or a PropertyArray of PropertyComponent (a list slot) with the allowedComponents allowlist. The slot’s name carries the convention.
Single section, default slot
The convention is to name the default slotcomponents — a list of view components rendered in the layout’s main area:
Multi-section layout
Distinct named slots produce distinct edit regions in editors. Settinginspector.visible: false keeps the slot itself out of the inspector — the layout surfaces the children directly inside the canvas instead.
Domain-specific slot names
For domain-specific layouts, name the slots after the things they contain. The same allowlist mechanism applies — only the named component types are accepted.Single-component slots
For a slot that holds exactly one child rather than a list, drop thePropertyArray wrapper:
Base fields (all property types)
Every property helper accepts the same base fields. Helper-specific options layer on top of these.| Field | Type | Description |
|---|---|---|
title | string | Display name in editors and the schema’s title. |
description | string | Natural-language description used by editors, validators, and LLMs. |
required | boolean | Whether the field is required at validation time. |
defaultValue | T | Type-specific default applied when no value is supplied. |
examples | T[] | Example values surfaced in editors and the generated schema. |
inspector | object | Editor configuration — see Inspector configuration. |
validation | object | Type-specific validation rules — see Validation rules. |
Inspector configuration
Theinspector object controls how the property appears in editors that render the schema as form controls. Some fields are common to every type; others are type-specific.
Conditional visibility
visible accepts a callback that receives the current resolved props and returns a boolean. Use it to show or hide a field based on the value of another property — for example, only showing a customColor field when colorMode === "custom":
showAdvanced immediately reveals or hides advancedSetting without a re-render of the parent.
Validation rules
Thevalidation object contains type-specific rules. Editors enforce these at input time; the runtime enforces them when resolving props before each render.
String:
PropertyBoolean, PropertyEnum, PropertyAsset, PropertyContent, PropertyComponent, and PropertyGroup don’t add helper-specific validation rules beyond the base required field. Group validation flows through the nested helpers.
Property type reference
The full set of helpers, with their runtime and schema mappings:| Helper | Runtime type | JSON Schema type | Notes |
|---|---|---|---|
PropertyString | string | string | Text input — singleline, multiline, or code controls. |
PropertyNumber | number | number | Real numbers; input or slider control. |
PropertyInteger | integer | integer | Integer-only; same shape as PropertyNumber. |
PropertyBoolean | boolean | boolean | Toggle / switch. |
PropertyEnum | enum | string/number enum | segmented or dropdown control. |
PropertyDate | date | string (ISO 8601) | Date picker. |
PropertyArray | array | array | Repeatable list, item type set by valueType. |
PropertyGroup | group | nested object | Nested fields under one collapsible section. |
PropertyAsset | asset | host-defined | Image / video / audio / 3D model picker; resolved by the host’s asset system. |
PropertyContent | content | host-defined | Reference to another content item; resolved by the host’s content system. |
PropertyComponent | component | recursive | Embedded child component, with allowedComponents allowlist for recursion. |
Schema generation for editors and LLMs
Every property helper has a deterministic mapping to JSON Schema. A consumer — an inspector form, a validator, an LLM — can read a component’sproperties schema and know exactly what a valid props object for that component looks like, without having to execute the body. This is the contract that makes BindJS usable as a target for editor-driven and LLM-generated UI.
What flows into the generated schema
- Type structure — each helper emits a corresponding JSON Schema fragment (
{ type: "string" },{ type: "integer" },{ type: "array", items: ... }). Nested helpers —PropertyArray({ valueType: PropertyGroup({...}) }), for example — compose into nested schema. title— included as the schema’stitle. Used as the field label in editors and as a human-readable identifier for LLM consumers.description— included as the schema’sdescription. This is the primary documentation surface for the field — what an LLM sees when reasoning about whether and how to populate it.required/defaultValue— included as the schema’srequiredarray entries anddefaultvalues.validation—minLength,maxLength,min,max,pattern,minItems,maxItems, anduniqueItemsflow through as the equivalent JSON Schema constraints.examples— included as the schema’sexamplesarray, which both editors and LLMs use as concrete guidance.inspectorhints — host-specific (control type, placeholder, help text). Editors that adopt them get richer forms; consumers that don’t can ignore them.
Recursive component schemas
PropertyComponent declares a slot for a nested component, optionally constrained to a specific set of component types via allowedComponents:
allowedComponents, generating a schema for that component’s own properties, and emitting the full set as a oneOf (or equivalent) in the parent’s schema. Walk repeatedly and you get a schema for an entire component subtree, rooted at any starting component.
The same applies to PropertyArray({ valueType: PropertyComponent({ allowedComponents: [...] }) }) — the array’s items schema is the recursive expansion.
Why this matters
The combination — typed properties,description as natural-language documentation, and allowedComponents as a navigable component graph — means a single root component carries enough information for an LLM to generate a valid invocation of the whole subtree, or for a host to validate an LLM’s output against the schema before rendering. Three things follow:
- Editors get inspectors for free. Any tool that can render a JSON Schema form can render an inspector for any BindJS component, without per-component handwritten UI.
- LLMs see structured documentation. The
descriptionfield on every property is the surface the LLM reads when reasoning about how to populate the schema. Treat it as the doc string, not as a label — every property the LLM is supposed to fill should describe its intent in prose. - Hosts can validate before rendering. An LLM’s response can be checked against the generated schema before it’s passed to a renderer. Invalid output is caught at the edge, not by the renderer crashing on a malformed prop.
How Metabind uses it
Metabind realizes this contract in two places. Content types in MCP App Studio. When a root BindJS component is bound to a content type in MCP App Studio, the CMS recursively walks the component’sproperties, expanding each PropertyComponent({ allowedComponents }) into the schemas of its allowed children, and so on. The result is a single JSON Schema that describes a valid content document for the whole subtree. The CMS uses it to render inspector forms, validate edits, and version-lock the schema with the published package — when you publish a new version of a component, content created against the old schema continues to validate against the version it was authored under.
Interactive Tools in the Cloud MCP App Server. An Interactive Tool declares an allowlist of components the LLM is permitted to render. The Cloud MCP App Server uses the same recursive walker over those components’ properties to produce the tool’s input schema, which is published as the tool’s inputSchema in MCP. The LLM sees the schema — including every description field — and generates a structured response. The server validates the response against the schema before the props ever reach the renderer.
In both cases, the only thing the consumer needs from your code is the properties shape. There’s no separate schema file to maintain — your component’s typed inputs and the schema editors and LLMs see are the same artifact.
What to read next
Components and bodies
How
properties fits inside defineComponent alongside body, metadata, and previews.Composition
Slots of child components, recursive
Self, and how component names resolve at runtime.Talking to the MCP host
How a BindJS view calls back into the chat — tool calls, messaging, display modes.
Layout — Stacks
VStack, HStack, ZStack — the layout primitives a component body returns.