12 min read • Updated a month ago

Manage product variants with Webflow CMS

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


Resources

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


Instructions

  1. 1

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

  2. 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. 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. 4

    In Webflow's Designer, go to your Products Collection Template (this may be called something else in your project).

  5. 5

    Give your add to cart Form a class name (ex: product-form). This class name will be used in a future step.

  6. 6

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

  7. 7

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

  8. 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. 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. 10

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

  11. 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. 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. 13

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

  14. 14

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

  15. 15

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

  16. 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. 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. 1

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

  2. 2

    Select your add to cart Form (ex: product-form) and go to the Element Settings Panel.

  3. 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. 1

    In your Products CMS Collection, add a multi-image field.

  2. 2

    Upload your images and sort them in the same order of your variant options (ex: Colors)

  3. 3

    In your Products Template, anywhere on the page, add a Collection List element.

  4. 4

    In the Collect List element Settings, set the Source as your new multi-image field.

  5. 5

    For each Collection List item, add an Image element and connect it to the CMS image.

  6. 6

    Give the Collection List Wrapper an ID of "foxy-images".

  7. 7

    Visually hide the Collection List.

  8. 8

    Give your main product image an ID of "foxy-image".

  9. 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).children(":first").val() ? $(this).prop("selectedIndex") + 1 : $(this).prop("selectedIndex");    var selectedImage = $("#foxy-images .w-dyn-item:nth-child("+ selectedIndex +") img").attr('src');          $("input[name='image']").val(selectedImage);    $("#foxy-image").attr("src", selectedImage);});

  10. 10

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

  11. 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} 

Valid Options

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.

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

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

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.


Need Help?

Did this article answer your questions? Need help with anything? Please click below to contact us.