Skip to main content

Build Your First Component

This quickstart guide will walk you through creating a complete BindJS component from scratch. By the end, you’ll have built a customizable button component with multiple states and previews.

What We’ll Build

We’re going to create a custom button component that:
  • Displays customizable text
  • Supports different color schemes
  • Has multiple size options
  • Shows different states in previews

Step 1: Define Metadata

Every component starts with metadata that describes what it is and how it should be organized:
const metadata = {
    title: "Custom Button",
    description: "A customizable button component with multiple styles",
    category: "Controls"
}
What this does:
  • title: The name shown in the component library
  • description: Helps others understand what the component does
  • category: Organizes the component (Controls, Display, Layout, etc.)

Step 2: Define Properties

Properties make your component configurable. Let’s add properties for the button’s text, color, and size:
const properties = {
    title: {
        type: "string",
        title: "Button Title"
    },
    color: {
        type: "enum",
        options: ["blue", "green", "red", "gray"],
        title: "Color"
    },
    size: {
        type: "enum",
        options: ["small", "medium", "large"],
        title: "Size"
    }
} satisfies ComponentProperties;
Property Types:
  • string: Text input
  • enum: Dropdown selection
  • number: Numeric input
  • boolean: Toggle switch
  • asset: Image/video/model upload

Step 3: Create the Body Function

The body function defines how your component looks and behaves:
const body = (props, children) => {
    // Determine height based on size
    const height = props.size === "small" ? 36 
                 : props.size === "large" ? 56 
                 : 44;
    
    // Determine padding based on size
    const horizontalPadding = props.size === "small" ? 16 
                            : props.size === "large" ? 32 
                            : 23;
    
    return Text(props.title || "Button")
        .fontWeight("semibold")
        .foregroundStyle(Color("white"))
        .padding('horizontal', horizontalPadding)
        .padding('vertical', 8)
        .frame({ height })
        .background(
            Capsule()
                .fill(Color(props.color || "blue"))
        )
        .shadow({ radius: 4, y: 2, color: Color('black').opacity(0.1) })
};
Key Concepts:
  • props: Access the property values
  • children: Components passed as children (not used here)
  • Modifiers: Chain methods like .fontWeight(), .padding(), .background()
  • Conditional logic: Adjust styling based on props

Step 4: Add Previews

Previews show different configurations of your component:
const previews = [
    Self({ title: "Click Me", color: "blue", size: "medium" })
        .previewName("Default"),
    
    Self({ title: "Submit", color: "green", size: "medium" })
        .previewName("Success"),
    
    Self({ title: "Delete", color: "red", size: "medium" })
        .previewName("Danger"),
    
    Self({ title: "Small", color: "blue", size: "small" })
        .previewName("Small Size"),
    
    Self({ title: "Large Button", color: "blue", size: "large" })
        .previewName("Large Size")
];
Preview Best Practices:
  • Show different states and variations
  • Use descriptive .previewName() labels
  • Include edge cases (small, large, different colors)

Step 5: Export the Component

Finally, export your component using defineComponent:
export default defineComponent({ 
    metadata, 
    properties, 
    body, 
    previews 
});

Complete Code

Here’s the full component code:
const metadata = {
    title: "Custom Button",
    description: "A customizable button component with multiple styles",
    category: "Controls"
}

const properties = {
    title: {
        type: "string",
        title: "Button Title"
    },
    color: {
        type: "enum",
        options: ["blue", "green", "red", "gray"],
        title: "Color"
    },
    size: {
        type: "enum",
        options: ["small", "medium", "large"],
        title: "Size"
    }
} satisfies ComponentProperties;

const body = (props, children) => {
    const height = props.size === "small" ? 36 
                 : props.size === "large" ? 56 
                 : 44;
    
    const horizontalPadding = props.size === "small" ? 16 
                            : props.size === "large" ? 32 
                            : 23;
    
    return Text(props.title || "Button")
        .fontWeight("semibold")
        .foregroundStyle(Color("white"))
        .padding('horizontal', horizontalPadding)
        .padding('vertical', 8)
        .frame({ height })
        .background(
            Capsule()
                .fill(Color(props.color || "blue"))
        )
        .shadow({ radius: 4, y: 2, color: Color('black').opacity(0.1) })
};

const previews = [
    Self({ title: "Click Me", color: "blue", size: "medium" })
        .previewName("Default"),
    Self({ title: "Submit", color: "green", size: "medium" })
        .previewName("Success"),
    Self({ title: "Delete", color: "red", size: "medium" })
        .previewName("Danger"),
    Self({ title: "Small", color: "blue", size: "small" })
        .previewName("Small Size"),
    Self({ title: "Large Button", color: "blue", size: "large" })
        .previewName("Large Size")
];

export default defineComponent({ metadata, properties, body, previews });

Using Your Component

Once defined, you can use your component anywhere:
// In another component's body
CustomButton({ title: "Click Me", color: "blue", size: "medium" })

// With modifiers
CustomButton({ title: "Submit", color: "green", size: "large" })
    .padding(16)
    .cornerRadius(8)

Next Steps

Congratulations! You’ve built your first BindJS component. Here’s what to explore next:

Add Interactivity

Make your button respond to clicks:
const body = (props, children) => {
    const [isPressed, setIsPressed] = useState(false)
    
    return Text(props.title || "Button")
        .fontWeight("semibold")
        .foregroundStyle(Color("white"))
        .padding('horizontal', 23)
        .padding('vertical', 8)
        .background(
            Capsule()
                .fill(Color(props.color || "blue"))
        )
        .scaleEffect(isPressed ? 0.95 : 1.0)
        .onTapGesture(() => {
            setIsPressed(true)
            setTimeout(() => setIsPressed(false), 100)
        })
};

Accept Children

Allow content to be passed as children:
const body = (props, children) => {
    return HStack({ spacing: 8 }, [
        ...children,  // Render children
        Text(props.title || "Button")
    ])
    .padding(16)
    .background(Capsule().fill(Color(props.color || "blue")))
};

// Usage
CustomButton({ color: "blue" }, [
    Image({ systemName: "star.fill" }),
    Text("Favorite")
])

Add More Properties

Expand your component with additional properties:
const properties = {
    title: { type: "string", title: "Title" },
    color: { type: "enum", options: ["blue", "green", "red"], title: "Color" },
    size: { type: "enum", options: ["small", "medium", "large"], title: "Size" },
    icon: { type: "string", title: "Icon Name" },  // New!
    disabled: { type: "boolean", title: "Disabled" }  // New!
} satisfies ComponentProperties;

Learn More