> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# What is BindJS

> Metabind's declarative cross-platform UI framework — one component definition rendering as React, SwiftUI, and Jetpack Compose

BindJS is Metabind's declarative cross-platform UI framework. You write a component once in a SwiftUI-inspired component language, and it renders as React on the web, SwiftUI on iOS, and Jetpack Compose on Android — without web views, and without per-platform forks.

It's the language MCP App Studio uses to author Interactive Tools. Those tools render inside MCP hosts like Claude Desktop, ChatGPT, and VS Code, and they render natively when embedded in your own iOS, Android, or web app via the Metabind Assistant SDK. The component definition is the same on every surface.

## Why BindJS

A growing class of applications needs to render server-defined or model-generated UI on multiple platforms. Interactive UI returned from MCP tool calls is one example. LLM-generated component output that ships into a mobile app is another. Most existing approaches in this space assume the renderer is a web view, so the UI ends up looking like a website wherever it appears.

BindJS takes a different position. A UI definition should be one declarative document that the host renders natively — as SwiftUI on iOS, Jetpack Compose on Android, or React on the web — rather than a web view embedded everywhere. The same `defineComponent` source produces native, 60 FPS UI on all three targets, with native gestures, native animations, and native typography. Authors stay in one source file; users get the platform their device runs on.

## The four pieces

A conforming BindJS implementation has four parts:

1. **Runtime** — a small JavaScript runtime that executes component code, manages state, and emits a JSON AST describing the UI tree.
2. **AST and component catalog** — the wire format that the runtime emits, plus the catalog of component and modifier names a renderer must understand (`Text`, `VStack`, `padding`, `onTapGesture`, and so on).
3. **Renderers** — per-platform implementations that walk the AST and produce native views: React, SwiftUI, and Jetpack Compose.
4. **Modifier pipeline** — the chained styling and behavior modifiers (`.padding()`, `.foregroundStyle()`, `.onTapGesture()`) applied in order before the renderer paints the underlying view.

The execution flow is straightforward: you author component code in JavaScript using the BindJS API; the runtime executes it with the BindJS globals (hooks, property helpers, animation builders) injected; component bodies return AST structures describing the UI tree; the renderer walks the AST and produces native views.

## A first look

A BindJS component packages a `body` render function and an optional `properties` schema into a `defineComponent` call exported as the module default. Properties are declared with helper functions like `PropertyString` and `PropertyBoolean`, and the body's `props` argument is typed against the schema with no manual interface needed.

```typescript theme={null}
const properties = {
  title: PropertyString({ title: "Title", required: true, defaultValue: "Welcome" }),
  showAction: PropertyBoolean({ title: "Show action", defaultValue: true }),
}

const body = (props, children) =>
  VStack({ spacing: 16 }, [
    Text(props.title)
      .font("headline")
      .foregroundStyle(Color("primary")),

    props.showAction
      ? Button("Get started", () => console.log("Tapped"))
      : Empty(),
  ])

export default defineComponent({
  metadata: { title: "Welcome card", description: "A simple example" },
  properties,
  body,
})
```

Two things to notice. First, the body's `props` is fully typed — `props.title` is `string`, `props.showAction` is `boolean` — inferred from the property schema. Second, layout and styling use function calls and method chaining instead of JSX. `VStack`, `Text`, and `Button` are functions that return components; modifiers like `.font()` and `.foregroundStyle()` chain off them.

## Where BindJS runs

BindJS components compile to a single bundle that reaches multiple rendering targets. Each target is a different renderer reading the same AST.

| Surface                                              | Renderer                                 | Platform                            |
| ---------------------------------------------------- | ---------------------------------------- | ----------------------------------- |
| MCP hosts (Claude Desktop, ChatGPT, VS Code, Cursor) | `@bindjs/renderer` in a sandboxed iframe | React on the web                    |
| Assistant SDK embedded in your iOS app               | `BindJSRuntime`                          | SwiftUI on iOS, macOS, and visionOS |
| Assistant SDK embedded in your Android app           | BindJS runtime                           | Jetpack Compose                     |
| Assistant SDK embedded in your web app               | `@bindjs/renderer`                       | React                               |

The component definition is identical across rows. The renderer translates each component and modifier to the platform's native equivalent — `VStack` becomes a SwiftUI `VStack` on iOS, a Compose `Column` on Android, and a flex container on the web. Same governance, same brand, same behavior; the renderer changes, the component does not. See [native rendering](/guides/concepts/native-rendering) for the full mapping.

## Authoring primitives

BindJS files declare one of two primitives, each exported as the module's default.

**`defineComponent`** is the canonical case. It packages a `body`, an optional `properties` schema, and optional `metadata`, `previews`, `thumbnail`, and `icon` fields into a single component definition. Almost every component you write — buttons, cards, lists, layouts — is a `defineComponent` call.

**`defineButtonStyle`** is the sibling primitive for custom button styles. It takes a `body` that receives a `configuration` (with the button's `label` and `isPressed`) and optional `props`, and is applied to a `Button` via the `.buttonStyle()` modifier. Use it when you want a reusable button appearance you can attach to any button in your project.

```typescript theme={null}
export default defineButtonStyle({
  body: (configuration, props) =>
    Capsule()
      .fill(Color(props?.color || "blue"))
      .overlay(configuration.label.foregroundStyle(Color("white")))
      .frame({ height: 44 })
      .opacity(configuration.isPressed ? 0.7 : 1),
})
```

Both primitives are typed in `metabind.d.ts`, the canonical TypeScript declaration file the runtime ships.

## What to read next

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/bindjs/quickstart">
    Build your first BindJS component in five steps.
  </Card>

  <Card title="Authoring components" icon="code" href="/bindjs/authoring/components">
    The `defineComponent` shape, metadata, body, and previews in depth.
  </Card>

  <Card title="MCP host integration" icon="grid" href="/bindjs/authoring/mcp-host">
    How a BindJS component becomes an Interactive Tool inside an MCP host.
  </Card>

  <Card title="Layout — Stacks" icon="layer-group" href="/bindjs/components/layout-stacks#vstack">
    A reference page for `VStack`, `HStack`, `ZStack`, and the lazy variants.
  </Card>
</CardGroup>
