Every BindJS component is a singleDocumentation Index
Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
Use this file to discover all available pages before exploring further.
defineComponent call exported as the module default. The shape is small: a render body function, a properties schema describing the inputs the body receives, and a handful of optional fields — metadata, previews, thumbnail, and icon — that surface the component in editors, design tools, and component pickers.
The same source produces native SwiftUI on iOS, Jetpack Compose on Android, and React on the web. You write one definition; the per-platform renderer turns the resulting AST into native views. See Native rendering for the runtime model.
A complete component
A typical component declares itsproperties and body as top-level constants and exports the defineComponent call. The body’s props argument is fully typed against the schema — there’s no manual ComponentProps interface to write.
propertiesdeclares the inputs the component accepts. Each entry uses a property helper (PropertyString,PropertyBoolean, etc.) that contributes both runtime validation and a JSON Schema fragment editors and LLMs can read. The full set of helpers is in Properties.bodyis the render function. It receives the typedpropsderived from the schema, plus achildrenarray of any nested components passed by a parent. It returns an AST built from BindJS components and modifiers — no JSX.defineComponentis the canonical export. The runtime callsbodywith the resolved props on every render and walks the returned AST.
useState and useEnvironment.
The body signature
Thebody field takes a function of the form:
props— an object whose type is inferred from thepropertiesschema viaInferProps<typeof properties>inside thedefineComponentoverload. You don’t declare it manually.children— aComponent[]array of components passed in by the caller, in the order they were supplied. Spread it into a layout (...children) or wrap it in a stack to render the contents.- Return value — a single
Component. Use VStack,HStack, orGroupto return multiple top-level views.
Inferred prop types
Given a schema, the body’sprops argument has exactly the shape you’d expect:
PropertyNumber for PropertyString — the body’s props.count becomes string automatically. The schema and the body type can’t drift.
Working with children
Thechildren argument is a Component[] of the components a parent passed in. Two patterns cover most cases — spread the array into a layout container, or wrap it in a stack to apply common modifiers across the whole group:
Heading, Paragraph, and Image children, for example — declare those slots as named properties using PropertyComponent and PropertyArray rather than relying on the implicit children array. See Slots of child components for the patterns.
How a body differs from React
A few mechanics carry over from React, but the surface is different:- No JSX. Components are function calls (
Text("Hi")), not elements (<Text>Hi</Text>). Modifiers chain off the result. - Return an AST, not React elements. The renderer walks the AST and produces native views — SwiftUI, Compose, or React DOM — depending on the platform.
- Props are typed via the schema. No
interface MyProps {...}; the helper-builtpropertiesrecord drives the inferred type. - State and context use runtime hooks.
useState,useStore, anduseEnvironmentare runtime-injected — see State and environment. - Styling and behavior come from modifiers.
.padding,.font,.onTapGesture, and the rest are method calls on a component, not props you pass in.
Optional fields
metadata, previews, thumbnail, and icon are optional. Components without them still render correctly; they exist to make the component discoverable, previewable, and recognizable inside editors and design tools.
metadata
Identification and discoverability information surfaced in editors, galleries, and documentation:
| Field | Type | Description |
|---|---|---|
title | string | Display name shown in component pickers and inspectors. |
description | string | One-sentence description used in galleries and tooltips. |
category | string | Optional category label used to group components. |
previews
Preview instances rendered in galleries and design tools. Use Self({...}) to instantiate the component itself with sample props, then attach a .previewName(...) so each variant is labeled:
Self’s prop types are inferred from the component’s own properties schema, so previews stay in sync with the inputs the body actually accepts.
thumbnail
A representative image used in component pickers. It accepts either an SVG string or a render function returning a Component:
icon
A short icon name used in menus and context menus. The host resolves the name against its icon registry:
Custom components are defineComponent calls
There’s no separate “custom component” primitive. A reusable component — a button, a card, a chart row — is defined with the same defineComponent shape as any other component. Once published, the component is callable from any other component in the same package.
A component that other components call doesn’t need a
metadata block, but providing one makes it discoverable in MCP App Studio and any editor that browses the package’s component list.defineButtonStyle — the sibling primitive
BindJS exposes one other authoring primitive: defineButtonStyle, used to define a custom button style applied via the .buttonStyle() modifier. It uses the same default-export pattern as defineComponent, but with a different body signature.
| Argument | Type | Description |
|---|---|---|
configuration | { label: Component, isPressed: boolean } | The label the caller supplied and the current pressed state. |
props | object (optional) | Style-level props. Type-inferred from properties if you declare them. |
.buttonStyle():
What to read next
Properties
The schema-and-validation layer behind the body’s typed
props.Composition
How components find each other at runtime, recursive
Self, and slots of child components.State and environment
Component-local state with
useState, shared state with useStore, and context with useEnvironment.Quickstart
A short walkthrough that puts a component on screen end-to-end.