> ## 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.

# defineComponent

> Defines a BindJS component with its body, metadata, properties, previews, and thumbnail.

```typescript theme={null}
defineComponent(config: {
    metadata?: Metadata;
    properties?: Record<string, PropertyField>;
    body: (props: ComponentProps, children: Component[]) => Component;
    thumbnail?: string | Component;
    previews?: (() => Component[]) | Component[];
}): ComponentCallable;
```

<ParamField path="config" type="object" required>
  The component configuration object.

  <Expandable title="config fields">
    <ParamField path="body" type="(props, children) => Component" required>
      The render function that returns the component's UI tree. Receives resolved props as the first argument and an array of child components as the second.
    </ParamField>

    <ParamField path="metadata" type="Metadata" optional>
      Display metadata for the Composer and component galleries.

      <Expandable title="Metadata fields">
        <ParamField path="title" type="string" optional>
          Display name shown in the Composer and component galleries.
        </ParamField>

        <ParamField path="description" type="string" optional>
          Brief description of the component's purpose.
        </ParamField>

        <ParamField path="category" type="string" optional>
          Grouping category for organizing components (e.g. "Layout", "Controls").
        </ParamField>

        <ParamField path="public" type="boolean" optional>
          Whether the component is publicly visible in the catalog.
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField path="properties" type="Record<string, PropertyField>" optional>
      Property schema defining the component's configurable inputs. Each key becomes a prop name, and the value is a property field constructor (`PropertyString`, `PropertyNumber`, `PropertyBoolean`, `PropertyEnum`, `PropertyArray`, `PropertyDate`, `PropertyComponent`, `PropertyAsset`, `PropertyContent`, `PropertyGroup`).
    </ParamField>

    <ParamField path="previews" type="Component[] | (() => Component[])" optional>
      Preview instances shown in galleries and documentation. Each preview is typically a `Self()` call with sample props. Use the `.previewName()` modifier to label individual previews.
    </ParamField>

    <ParamField path="thumbnail" type="string | Component" optional>
      A thumbnail for the component. Can be an SVG string or a component that renders a visual preview.
    </ParamField>
  </Expandable>
</ParamField>

## Returns

A `ComponentCallable` that serves as both the component's default export and a callable function. When called with props, it creates an instance of the component. The returned value also carries the `metadata`, `properties`, `thumbnail`, and `previews` for the Composer to read.

## Usage

### Minimal component

```typescript theme={null}
const body = () => Text("Hello, World!")

exports.default = defineComponent({ body })
```

### With properties

```typescript theme={null}
const body = (props) => {
    return VStack([
        Text(props.title).font("headline"),
        Text(props.subtitle).foregroundStyle(Color("secondary"))
    ])
}

const properties = {
    title: PropertyString({ defaultValue: "Hello" }),
    subtitle: PropertyString({ defaultValue: "World" })
}

exports.default = defineComponent({ body, properties })
```

### With metadata and previews

```typescript theme={null}
const body = (props) => {
    return VStack({ spacing: 12 }, [
        Image({ url: props.imageURL })
            .resizable()
            .scaledToFill()
            .frame({ height: 200 })
            .clipShape("roundedRectangle", { cornerRadius: 12 }),
        Text(props.title).font("headline"),
        Text(props.description)
            .foregroundStyle(Color("secondary"))
    ])
}

const properties = {
    title: PropertyString({ defaultValue: "Card Title" }),
    description: PropertyString({ defaultValue: "A short description." }),
    imageURL: PropertyString({ defaultValue: "https://picsum.photos/400/200" })
}

const metadata = {
    title: "Info Card",
    description: "A card with an image, title, and description.",
    category: "Content"
}

const previews = [
    Self({
        title: "Welcome",
        description: "Get started",
        imageURL: "https://picsum.photos/400/200"
    }).previewName("Default"),
    Self({
        title: "Error",
        description: "Something went wrong",
        imageURL: "https://picsum.photos/400/200"
    }).previewName("Error State")
]

exports.default = defineComponent({ body, metadata, properties, previews })
```

### With children

```typescript theme={null}
const body = (props, children) => {
    return VStack({ spacing: props.spacing }, children)
        .padding(16)
        .background(Color(props.backgroundColor))
        .cornerRadius(12)
}

const properties = {
    spacing: PropertyNumber({ defaultValue: 8 }),
    backgroundColor: PropertyString({ defaultValue: "background" })
}

exports.default = defineComponent({ body, properties })
```

### With thumbnail

```typescript theme={null}
const body = (props) => Text(props.label).font("body")

const properties = {
    label: PropertyString({ defaultValue: "Hello" })
}

const thumbnail = '<svg viewBox="0 0 24 24"><text x="4" y="16" font-size="12">Aa</text></svg>'

exports.default = defineComponent({ body, properties, thumbnail })
```

## Notes

* The `body` function is called on every re-render. Avoid expensive computations inside it, or use hooks like [useState](/bindjs/functions/useState) to cache values.
* `Self()` is available inside `body` and in `previews` to create instances of the component being defined. Props passed to `Self()` are typed based on the `properties` schema.
* The component must be assigned to `exports.default` to be recognized by the BindJS runtime.
* Properties can also be defined as a factory function `() => Record<string, PropertyField>` for dynamic schema generation.

## See Also

* [defineButtonStyle](/bindjs/functions/defineButtonStyle) -- for defining custom button styles
* [useState](/bindjs/functions/useState) -- component-local state
* [useStore](/bindjs/functions/useStore) -- global shared state
