Manage product variants with Webflow CMS

Use Webflow CMS to dynamically control what variant groups and options are available for your products.

Note

This article assumes you have already setup your Products CMS Collection and add to cart forms. More information here. Also, this approach does not work with our Webflow CMS inventory integration. If you need to automatically update inventory counts for variants, please contact us.

Resources

Please use the following links to get a visual on how dynamic variants will be setup:

Instructions

  1. Add a single line plain text field for each variant group (ex: Colors, Sizes, etc.)

  2. For each product, add a comma separated list of values for each variant group. Simply leave a variant group field blank if no options are available.

  3. To have a variant option modify the price, weight, code, or category, add a valid product option modifier to that option or options.

  4. In Webflow's Designer, go to your Products Collection Template (this may be called something else in your project).
  5. Give your add to cart Form a class name (ex: product-form). This class name will be used in a future step.

  6. Add a select field (and optionally a label) for each variant group.

  7. Give each select field a a unique class name (ex: colors).

  8. Give each select field a Name (ex: Color). This label will show in the cart. If your Name has spaces, use an underscore instead of a space (ex: Primary_Color).

  9. Remove all select field choices. Alternatively, you can leave the first choice as a label. If you do this, you'll want to set the value as empty and make the select field required.

  10. Right inside your add to cart Form Block, add an HTML Embed element.

  11. Inside of the HTML Embed element, copy/paste the snippet below:

    <script>
      var options = options || {};
      options["FORM CLASS NAME"] = {};
      options["FORM CLASS NAME"]["SELECT CLASS NAME"] = "CMS FIELD";
    </script>

  12. In the snippet, replace each FORM CLASS NAME (on line 3 & 4) with the class name you gave your add to cart Form (ex: product-form) in Step 5.

    Please note that Webflow exports class names as lowercase, even if you use uppercase letters in the Designer. So be sure to input your form class name as all lowercase letters.

  13. On line 4, replace SELECT CLASS NAME with the class name of your first variant group (ex: colors).

  14. On line 4, replace CMS FIELD with the corresponding CMS field for your first variant group.

  15. For each additional variant group, copy/paste line 4, modify the class name, and change the CMS field.

  16. In your Products Template Settings, copy/paste the following snippet into the "Before </body> tag" section.

    <script>    
        var currency_symbol = '$';
        var modifier_text_summary = true;
    
        var foxy_pattern=/^([^\{,]+)(\{((?:[pwcy][+:-][^\|\}]+\|?)+)\})?$/,modifier_pattern=/^p([+:-])([\d.]+)/;function convertSlugAsNeeded(a){return a.replace(/^\d/,function(a){return"\\"+a.charCodeAt(0).toString(16)+" "})}
        for(var slug in options){var target=document.querySelectorAll("form."+convertSlugAsNeeded(slug))[0];if(target)for(var key in options[slug])if(""!=options[slug][key]){var select=target.querySelectorAll("select."+convertSlugAsNeeded(key))[0];if(select)for(options_arr=options[slug][key].split(","),i=0;i<options_arr.length;i++){var curr=options_arr[i].trim(),option=curr.match(foxy_pattern),modifiers=[];option[1]=option[1].trim();var option_text="";option[3]&&(modifiers=option[3].split("|"));if(modifiers)for(var j=
        0;j<modifiers.length;j++){var price_modifier=modifiers[j].match(modifier_pattern);!price_modifier||"undefined"!==typeof modifier_text_summary&&!0!==modifier_text_summary||(option_text+=" (",option_text+=":"==price_modifier[1]?"":price_modifier[1],option_text+=currency_symbol+price_modifier[2],option_text+=")")}select.options[select.options.length]=new Option(option[1]+option_text,option[0])}}};
    
        var pricemod_regex=/[{\|]p([+\-:])([\d\.]+)(?:\D{3})?(?=[\|}])/,id_regex=/^(\d+):/,FC=FC||{};FC.onLoad=function(){FC.client.on("ready.done",initDynamicPrice)};
        function initDynamicPrice(){ADJUST={};$("input,select").off("change.foxy-dynamic-price");$('form[action*="'+FC.settings.storedomain+'"]').each(function(){var b=$(this),d="",g={products:{}};$(this).find("[name='name'],[name^='name||'],[name$=':name'],[name*=':name||']").each(function(){var k=getId(this.name),c=k?k+":":"",e=parseFloat(b.find("[name='"+c+"price'],[name^='"+c+"price||']").first().val());e={id:k,code:"",base_price:isNaN(e)?0:e,quantity:1,attributes:{},has_quantity:!1};var h=b.find("[name='"+
        c+"quantity'],[name^='"+c+"quantity||']");c=b.find("[name='"+c+"code'],[name^='"+c+"code||']");0<c.length&&(e.code=clearHash(c.first().val()),""===d&&(d=e.code));if(0<h.length){c=0;var l=getElementType(h);-1<["select","text"].indexOf(l)?(e.has_quantity=!0,c=parseFloat(clearHash(h.val()))):-1<["radio","checkbox"].indexOf(l)&&(e.has_quantity=!0,1==h.filter(":checked").length&&(c=parseFloat(clearHash(h.filter(":checked").val()))));isNaN(c)&&(c=0);e.quantity=c}g.products[k]=e});b.attr("data-fc-form-code")&&
        (d=b.attr("data-fc-form-code"));""!==d&&($(this).find("input,select").each(function(){var b=getId(this.name),c=getName(this.name),e=getElementType($(this));if("quantity"==c)$(this).data("fc-adjust-for",d).on("change.foxy-dynamic-price",function(){var c=0;if(-1<["select","text"].indexOf(e)||-1<["radio","checkbox"].indexOf(e)&&$(this).is(":checked"))c=parseFloat(clearHash(this.value));isNaN(c)&&(c=0);ADJUST[$(this).data("fc-adjust-for")].products[b].quantity=c;recalcTotal()});else if("price"==c&&"hidden"!=
        e)$(this).data("fc-adjust-for",d).on("change.foxy-dynamic-price",function(){var c=0;if(-1<["select","text"].indexOf(e)||-1<["radio","checkbox"].indexOf(e)&&$(this).is(":checked"))c=parseFloat(clearHash(this.value));isNaN(c)&&(c=0);ADJUST[$(this).data("fc-adjust-for")].products[b].base_price=c;recalcTotal()});else if("SELECT"==this.tagName){var h=!1;$(this).children("option").each(function(){-1<this.value.search(pricemod_regex)&&(h=!0)});h&&($(this).data("fc-adjust-for",d),g.products[b].attributes[clearHash(this.name)]=
        clearHash(this.value),$(this).on("change.foxy-dynamic-price",function(){ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value);recalcTotal()}))}else if(-1<this.value.search(pricemod_regex))switch($(this).data("fc-adjust-for",d),$(this).attr("type")){case "checkbox":$(this).is(":checked")?g.products[b].attributes[clearHash(this.name)]=clearHash(this.value):g.products[b].attributes[clearHash(this.name)]="";$(this).on("change.foxy-dynamic-price",function(){$(this).is(":checked")?
        ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value):ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]="";recalcTotal()});break;case "radio":g.products[b].attributes.hasOwnProperty(clearHash(this.name))||(g.products[b].attributes[clearHash(this.name)]=""),$(this).is(":checked")&&(g.products[b].attributes[clearHash(this.name)]=clearHash(this.value)),$("[name='"+this.name+"']").data("fc-adjust-for",d).on("change.foxy-dynamic-price",
        function(){ADJUST[$(this).data("fc-adjust-for")].products[b].attributes[clearHash(this.name)]=clearHash(this.value);recalcTotal()})}}),ADJUST[d]=g)});recalcTotal()}function clearHash(b){return b.replace(/\|\|[\d\w]+(?:\|\|open)?$/,"")}function getNameParts(b){b=clearHash(b);return b.match(/(?:(\d+):)?(.*)/)}function getId(b){b=getNameParts(b);id_regex.test(this.name)&&(prefix=parseInt(this.name.match(id_regex)[0]));return void 0===b[1]?0:parseInt(b[1])}
        function getName(b){return getNameParts(b)[2]}function getElementType(b){if("SELECT"==b[0].tagName)return"select";if("INPUT"==b[0].tagName)switch(b.attr("type").toLowerCase()){case "text":case "number":case "tel":return"text";default:return b.attr("type").toLowerCase()}}
        function recalcTotal(){for(f in ADJUST){var b=0,d=0;for(p in ADJUST[f].products){var g=ADJUST[f].products[p].base_price,k=0;for(a in ADJUST[f].products[p].attributes){var c=ADJUST[f].products[p].attributes[a].match(pricemod_regex);if(c)switch(c[1]){case ":":g=parseFloat(c[2]);break;case "+":k+=parseFloat(c[2]);break;case "-":k-=parseFloat(c[2])}}g+=k;g*=ADJUST[f].products[p].quantity;b+=g;d+=ADJUST[f].products[p].quantity}"function"===typeof fcFormatPrice&&(b=fcFormatPrice(b,f));"function"===typeof fcFormatQuantity&&
        (d=fcFormatQuantity(d,f));b="object"==typeof FC&&FC.hasOwnProperty("json")&&FC.json.config.hasOwnProperty("currency_format")?jQuery.trim(FC.util.money_format(FC.json.config.currency_format,b)):b.formatMoney(2);$("."+f+"_total").html(b);$("."+f+"_total_quantity").html(d)}}
        Number.prototype.formatMoney=function(b,d,g){var k=this;b=isNaN(b=Math.abs(b))?2:b;d=void 0==d?".":d;g=void 0==g?",":g;var c=0>k?"-":"",e=parseInt(k=Math.abs(+k||0).toFixed(b))+"",h=3<(h=e.length)?h%3:0;return c+(h?e.substr(0,h)+g:"")+e.substr(h).replace(/(\d{3})(?=\d)/g,"$1"+g)+(b?d+Math.abs(k-e).toFixed(b).slice(2):"")};
    
        $(document).ready(function() {
            $(".w-condition-invisible").each(function() {
                $(this).remove();
            });
    
            $('input[name="quantity"]').val("1").attr("min", "1");
        });
    </script>
  17. Publish and test.

Conditionally Show/Hide Variants

To conditionally show/hide variant select fields, simply add a conditional visibility setting to the select field and corresponding label:

Real-time Price Display

By default, your price display will not change based on chosen variants and quantity. Please follow the instructions below to display the updated price in real-time.

  1. Give your price display element a class name of "product_total".

  2. Select your add to cart Form (ex: product-form) and go to the Element Settings Panel.
  3. With the form selected, add a custom attribute with the Name "data-fc-form-code" and Value "product".

Real-time Image Change

There may be times where you want to change the displayed image (and image that's passed to the cart) based on a chosen variant. Please follow the instructions below to do this. These instructions are for one variant. If you have a combination of variants that determine the displayed image, please contact us.

  1. In your Products CMS Collection, add a multi-image field.
  2. Upload your images and sort them in the same order of your variant options (ex: Colors)
  3. In your Products Template, anywhere on the page, add a Collection List element.
  4. In the Collect List element Settings, set the Source as your new multi-image field.
  5. For each Collection List item, add an Image element and connect it to the CMS image.
  6. Give the Collection List Wrapper an ID of "foxy-images".
  7. Visually hide the Collection List.
  8. Give your main product image an ID of "foxy-image".
  9. In your Products Template Settings "Before </body> tag" section, right after

    $('input[name="quantity"]').val("1").attr("min", "1");

    Copy/paste the following:

    $("#foxy-image").removeAttr("srcset");$(".CHANGE").change(function() {
        var selectedIndex = $(this).prop("selectedIndex") + 1;
        var selectedImage = $("#foxy-images .w-dyn-item:nth-child("+ selectedIndex +") img").attr('src');      
        $("input[name='image']").val(selectedImage);
        $("#foxy-image").attr("src", selectedImage);
    });

  10. Replace CHANGE with the class name of the variant select field you want to trigger the image change (ex: "colors").
  11. Publish and test.

Optional Settings

By default, USD will be used for the currency. In addition, a summary will be displayed if a variant option affects the price (ex: +$5). These settings can be modified in your Products Template Settings > "Before </body> tag" section:

var currency_symbol = '$';
var modifier_text_summary = true;

Product Option Modifiers

Product option modifiers allow you to modify the price, weight, code, or category when another option is set. Modifiers are placed inside curly brackets {} at the end of your product option, and can add to +, subtract from -, or set : new values to the modified option. Multiple modifiers can be chained together with the “pipe” symbol |

Example

{p+1.50|w-1|c:01a|y:teeny_category} 

Note

If you're working with a product code modifier, be sure your code input comes before your product option that modifies it. Modifiers cannot be applied to standard product options (ex: price or name).

Valid Options

p

Modifies: Price
Increasing: Small{p+5}
Decreasing: Small{p-5}
Setting: Small{p:5}
Notes: If working with multicurrency, you may need to append the currency code to the price as required, such as {p+5CAD}. If no currency code is specified, it's assumed the price is in the store currency.

w

Modifies: Weight.
Increasing: Small{w+5}
Decreasing: Small{w-5}
Setting: Small{w:5}

c

Modifies: Code
Setting:  Small{c:bar} would yield a code of "bar".
Appending: Small{c+bar} would add "bar" to your product base code and any other code modifier options

y

Modifies: Category
Setting: Small{y:bar} would yield a category of "bar".
Notes: This can be especially handy in donation forms that allow both single and recurring donations.

In This Article

Related Articles

Create a purchase form with Webflow's Form Builder
Getting Started with Foxy + Webflow
Create a purchase button in Webflow
Manage product inventory with Webflow's CMS

Need help?

Can't find the answers you're looking for? Our team is here to help. Please click below to contact us.

Contact Us