---
title: "Cart & Variant Selection"
date: 2026-07-02T14:35:00+01:00
author: John Henry Donovan
canonical_url: "https://johnhenry.ie/plugins/bundle-builder/docs/guide/cart-variant-selection"
section: Plugins
---
esc

↑↓ to navigate↵ to selectesc to close

Menu On this page 

- [Return to top](#)

Documentation

Documentation

# Cart &amp; Variant Selection

A bundle goes into the cart as a **single line item** at the bundle price. The customer's per-component variant choices ride along with it.

## Adding to the cart

Post the bundle's `purchasableId` plus a variant for each component under `options[bundleProducts][<productId>]`:

```twig
<form method="post">
  {{ csrfInput() }}
  {{ actionInput('commerce/cart/update-cart') }}
  {{ hiddenInput('purchasableId', bundle.id) }}
  {{ hiddenInput('qty', 1) }}

  {% for component in bundle.getProducts() %}
    {% set product = component.product %}
    <select name="options[bundleProducts][{{ product.id }}]">
      {% for variant in product.variants %}
        <option value="{{ variant.id }}">{{ variant.title }}</option>
      {% endfor %}
    </select>
  {% endfor %}

  <button type="submit">{{ 'Add to cart'|t }} · {{ bundle.priceAsCurrency }}</button>
</form>
```

## What happens on add

The bundle's `populateLineItem()` runs the cart service, which:

1. **Resolves** the chosen variant for each component (falling back to the product's default variant if the posted one is invalid or missing).
2. **Checks stock**: adds a cart error if an inventory-tracked variant can't cover the quantity requested.
3. Writes **readable options** to the line item (e.g. `Collection: Standard`, `Skippo Bag: Large`) so the cart, checkout and CP order all show titles rather than IDs.
4. Freezes the selections into the line item **snapshot**, for the order record and the inventory decrement.

## Distinct selections

Different variant selections produce different line-item options, so they stay as **separate cart lines**; they won't merge quantities.

On this page
