Only show selected variant images in Shopify for better UX

Shopify’s default themes will show all variant images on a product page. If you have a product with many variant combinations it will result in a very repetitive image gallery (if each variant has an individual image attached). A little custom code can tweak this and result in a much better user experience by only showing selected variant images, as well as the general product image not attached to a variant.

The following instructions will work for Shopify’s Debut theme.

1. Prepare templates/product.dynamic_thumbnails.liquid with metadata on the variant images

Find the {% for media in product.media %} loop and add the bold code below to attach the image variant IDs as data attributes for each gallery item. Here the .product-single__thumbnails-item is extended, so take care that the closing HTML tag is correct. Later, a JavaScript mechanism will grab the attributes’ images to show and hide the pictures.

<div class="product-single__thumbnails-slider-track" data-slider-track>
{% endif %}

{%- assign variant_images = product.images | where: 'attached_to_variant?', true | map: 'src' -%}
  {% for media in product.media %}
    <li class="product-single__thumbnails-item product-single__thumbnails-item--{{ section.settings.media_size }} {% if enable_thumbnail_slides == true %} product-single__thumbnails-item-slide{% endif %} js"{% if enable_thumbnail_slides == true %} data-slider-slide-index="{{ forloop.index0 }}" data-slider-item{% endif %}>
    <li class="product-single__thumbnails-item product-single__thumbnails-item--{{ section.settings.media_size }} {% if enable_thumbnail_slides == true %} product-single__thumbnails-item-slide{% endif %} js"{% if enable_thumbnail_slides == true %} data-slider-slide-index="{{ forloop.index0 }}" data-slider-item{% endif %}
    {%- if variant_images contains media.src -%}
      {%- assign variants = '' | split: '' -%}
      {%- assign tmp_variants = product.images | where: 'src', media.src | map: 'variants' -%}
      {%- for variant in tmp_variants -%}
        {%- assign variants = variants | concat: variant -%}
      {%- endfor -%}
      data-variant-ids={{ variants | map: 'id' | uniq | join: ',' }}
    {%- endif -%}>
<a href="{{ media.preview_image | img_url: product_image_zoom_size, scale: product_image_scale }}"
  class="text-link product-single__thumbnail product-single__thumbnail--{{ section.id }}"
  data-thumbnail-id="{{ section.id }}-{{ media.id }}"

2. Update assets/theme.js to conditionally show and hide variant images based on the selected variant

Here we extend the Variants object, thus the following code has to be added within the Variants.prototype = Object.assign({}, Variants.prototype, { ... }) block. Add the following function, e.g. below the _updateImages function:

/**
 * Update thumbnails visibility
 *
 * @param  {object} variant - Currently selected variant
 */
_updateThumbnails: function(variant) {
  const allThumbs = document.querySelectorAll('[data-variant-ids]');
  const keepThumbs = [];

  allThumbs.forEach(function(el) {
    const variantIds = el.dataset.variantIds.split(',');
    if (!variantIds.length) {
      return;
    }

    if (variantIds.includes('' + variant.id)) {
      keepThumbs.push(el);
    }
  });
  
  if (!keepThumbs.length) {
    return;
  }
  
  allThumbs.forEach(function(el) {
    el.classList.add('hide');
  });
      
  keepThumbs.forEach(function(el) {
    el.classList.remove('hide');
  });
},

This will update the thumbnail visibility based on the variant argument.

The _updateThumbnails function has to be added in two places:

First, in the _onSelectChange event handler, which is called whenever the user changes a variant (changes are bold).

 /**
 * Event handler for when a variant input changes.
 */
_onSelectChange: function() {
  var variant = this._getVariantFromOptions();

  this.container.dispatchEvent(
    new CustomEvent('variantChange', {
      detail: {
        variant: variant
      },
      bubbles: true,
      cancelable: true
    })
  );

  if (!variant) {
    return;
  }

  this._updateMasterSelect(variant);
  this._updateImages(variant);
  this._updateThumbnails(variant);
  this._updatePrice(variant);
  this._updateSKU(variant);
  this.currentVariant = variant;

  if (this.enableHistoryState) {
    this._updateHistoryState(variant);
  }
},

Second, in the slate.Variants = (function() { ... }))() block, when the page is loaded to apply the correct initial state:

/**
 * Variant constructor
 *
 * @param {object} options - Settings from `product.js`
 */
 function Variants(options) {
  this.container = options.container;
  this.product = options.product;
  this.originalSelectorId = options.originalSelectorId;
  this.enableHistoryState = options.enableHistoryState;
  this.singleOptions = this.container.querySelectorAll(
    options.singleOptionSelector
  );
  this.currentVariant = this._getVariantFromOptions();

  this.singleOptions.forEach(
    function(option) {
      option.addEventListener('change', this._onSelectChange.bind(this));
    }.bind(this)
  );
  
  // Initialize thumbnails
  this._updateThumbnails(this.currentVariant);
}

Now the selected variant images and general product images will be shown!