Is it possible to have separate variant option fields on a front end add to cart form?

For example… have a separate select field for Color and a separate select field for Size?


It's definitely possible and doesn't really need any custom back end code to be done in general.

Ultimately all add to cart options in Commerce are just a simple case of POSTing the variant id and the quantity of that variant.

Generally I would do this all in twig & a bit of JS.

E.g. In twig I wouldt loop through all variants (checking for stock as I go) and build up a JSON array of all the combinations of colour & size, and their availability due to stock. I'd then 'pass this into' my form as a data-variants attribute.

Actually building the selects is slightly trickier because one will depend on the other but basically you'd probably want some JS that listens to your selects and looks at your passed in array to see if a particular size and colour combination was in stock - if a chosen combination is not, some sort of visual indication and disable the add to cart button, otherwise if it is in stock the form just posts the variant id and the qty and away you go.

Updated with some sample code:

Here's some untested/pseudo code that should get you on the right track:

so the important thing to understand is that the descriptive data of a variant is really a completely separate concern to the sku of the variant - which in turn is not actually all that relevant to commerce - the variant id is really all that matters in the end in terms of addToCart. For specifiying the variant data you can do it any way you like really but in this case if it's just say 3 colours and 3 sizes, then a simple approach would be a dropdown for each of those, with handles colour and size say.

You then just make the 9 variants, choose the colour and size for each, and enter the sku for each.

Then, you want to make the JSON for use in the JS. (It's hard for me to see how you'd have a good UX without JS here, to be honest).

Here the |jsonencode filter is handy. Something along the lines of:

{% set stockArray = [] %}
{% for variant in product.variants %}
    {% set stockArray = stockArray|merge([{variantId:variant.id, 
                                          variantColourAndSize :variant.colour ~ "-" ~ variant.size,
                                          }]) %}
{% endfor %}

Then you output your form and pass this consolidated data in to it so it's accessible in the JS:

<form id="OrderForm" 
    data-stockarray="{{ stockArray|json_encode() }}"

<selects here>

The rest is really just JS operating on that json data - this is thoroughly pseudo code since it's Friday night :) - but should be enough to get the idea:

var $form = document.getElementById('OrderForm');
var stockArray = $form.dataset.stockarray;
// Listen to your selects and look up in this array whether the matching colour/size variant is in stock/has unlimited stock
var variantKey = $select1.val() + "-" + $select2.val();
if(stockArray[variantKey].variantIsUnlimited || stockArray[variantKey].variantStock > 0 ){
// Enable/Disable your add to cart button accordingly
else {
  • Any code examples to go along with your explanation? – Luke Pearce May 20 '16 at 8:45
  • If @jamescgarrett needs them, I'll be happy to help him along, here or in Slack. When it comes to Commerce I'm pretty much always here or there :) – Jeremy Daalder May 20 '16 at 9:01
  • This put me on the right track and I have it working! Thank you very much! Just being curious do you see any issue with the way I'm looping through the variant options? {% for variant in product.variants[0].color.options %}<option value="{{variant.value}}">{{variant.label}}</option>{% endfor %} – jamescgarrett May 20 '16 at 16:37
  • That should be fine I think, you're just grabbing the options from the first variant so I can't see any reason not to do that really. – Jeremy Daalder May 21 '16 at 0:11
  • @JeremyDaalder, you're direction here has been really helpful for me too. I'm noticing though with the stockArray, using merge just overwrites the previous variant since all the keys are the same so I only get the last variant back in the JSON string. Is there a way to append this instead? – Tim Knight May 17 '17 at 1:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.