# BundleBuilder

> Frequently Bought Together component. Shows 2-3 complementary products with a bundle discount CTA.

Canonical URL: https://fa7e86e3d553:3005/islands/commerce/bundle-builder

> Frequently Bought Together component. Shows 2-3 complementary products with a bundle discount CTA. Emits bundle:add event. Place below BuyBox for AOV uplift.

## Preview

**Category**: Commerce

## Props

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| mainProduct | object | Yes | - | Main product object with id, title, price, image |
| recommendations | array | Yes | - | Array of recommended products with id, title, price, image, handle (optional), selected (optional) |
| bundleDiscount | object | No | - | Discount object with type ('percentage' or 'fixed'), value (number) |
| title | string | No | "Frequently bought together" | Section title |
| layout | enum | No | "horizontal" | Layout style: "horizontal" or "stacked" |
| showCheckboxes | boolean | No | true | Whether to show checkboxes for selecting items |

## Embed Example

```html
<lx-island name="BundleBuilder">
  <script type="application/json">
{
  "mainProduct": {
    "id": "example-id",
    "title": "example-title",
    "price": "example-price",
    "image": "example-image"
  },
  "recommendations": [
    {
      "id": "example-id",
      "title": "example-title",
      "price": "example-price",
      "image": "example-image"
    }
  ]
}
  </script>
</lx-island>
```

## Usage Patterns

**Standard placement (below BuyBox on PDP):**

```html
<lx-island name="BundleBuilder">
  <script type="application/json">
{
  "mainProduct": {
    "id": "example-id",
    "title": "example-title",
    "price": "example-price",
    "image": "example-image"
  },
  "recommendations": [
    {
      "id": "example-id",
      "title": "example-title",
      "price": "example-price",
      "image": "example-image"
    }
  ]
}
  </script>
</lx-island>
```

**Horizontal layout (3-product row, most common):**

```html
<lx-island name="BundleBuilder">
  <script type="application/json">
{
  "mainProduct": {
    "id": "example-id",
    "title": "example-title",
    "price": "example-price",
    "image": "example-image"
  },
  "recommendations": [
    {
      "id": "example-id",
      "title": "example-title",
      "price": "example-price",
      "image": "example-image"
    }
  ]
}
  </script>
</lx-island>
```

**Stacked layout (vertical, mobile-first):**

```html
<lx-island name="BundleBuilder">
  <script type="application/json">
{
  "mainProduct": {
    "id": "example-id",
    "title": "example-title",
    "price": "example-price",
    "image": "example-image"
  },
  "recommendations": [
    {
      "id": "example-id",
      "title": "example-title",
      "price": "example-price",
      "image": "example-image"
    }
  ]
}
  </script>
</lx-island>
```

Note: `bundleDiscount.value` is in cents when type is "fixed" (1000 = $10 or ₹10 off).

## Events

| Event | Direction | Payload |
|-------|-----------|---------|
| bundle:add | Emits | `{items: [{variantId: string, quantity: number}], discount: {type: string, value: number}}` when "Add Bundle to Cart" clicked |
| bundle:change | Emits | `{selectedIds: string[], totalPrice: number, savings: number}` when checkboxes toggled |

## Tips

- Place directly below [BuyBox](/islands/commerce/buy-box) or in a dedicated "Complete Your Routine" / "Frequently Bought Together" section
- Use 2-3 recommendations (more than 3 overwhelms, less than 2 is ineffective)
- `layout:"horizontal"` is standard for desktop PDP (3-product row)
- `layout:"stacked"` is better for mobile or sidebar placement
- Set `selected:true` in recommendations to pre-check items (e.g., pre-select the 2nd recommendation as "Most Popular")
- `bundleDiscount.type:"percentage"` (e.g., 15% off bundle) is more common than "fixed"
- `bundleDiscount.type:"fixed"` value in cents (e.g., 1000 = $10 off)
- Shows product thumbnails, titles, prices, and checkboxes
- Displays total price, savings, and "Add Bundle to Cart" CTA
- Emits `bundle:add` -- triggers [CartDrawer](/islands/cart/cart-drawer) or [DrawerShell](/islands/cart/drawer-shell) (cart profile) with all selected items at once
- Alternative to [ProductCarousel](/islands/commerce/product-carousel) when you want a fixed, curated set instead of scrolling

## Stylable Parts

Target internal elements via `data-part` selectors in section CSS:

| Part | Description |
|------|-------------|
| `root` | Outermost container |

```css
/* Example: custom styling */
[data-section-id='SECTION'] [data-part='root'] {
  border-radius: 1rem;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
```

## Related Islands

- [BuyBox](/islands/commerce/buy-box) — Place BundleBuilder below BuyBox for AOV uplift
- [CartDrawer](/islands/cart/cart-drawer) — Triggered by `bundle:add` event
- [ProductCarousel](/islands/commerce/product-carousel) — Alternative for scrolling "You May Also Like" instead of fixed bundle
- [QuantityBreaks](/islands/commerce/quantity-breaks) — Alternative for volume discounts on same product (not cross-sell)
