I need help with variant based image switching. I want customers to click black and see all black mockups, how can I achieve this?
I am using refresh theme
I need help with variant based image switching. I want customers to click black and see all black mockups, how can I achieve this?
I am using refresh theme
Hey @LEGACY3
Refer to this topic below:
Dawn and Refresh pretty much has the same backend so this might be helpful, if it doesn’t works then let me know and I’ll be happy to provide you a personalized solution.
Best,
Moeed
here is the entire code-block and steps to achieve your requirment
firstly, add the below code in the main-product.liquid file:
{
"type": "header",
"content": "Selected Variant Images"
},
{
"type": "checkbox",
"id": "enable_group_media",
"label": "Enable",
"default": false
},
{
"type": "text",
"id": "media_grouping_option",
"label": "Variant option name",
"default": "Color",
"info":"Attach the first image of each group to a variant."
}
then update the product-info.js file with the below code
const variantValues = variant.options
const mediaGallery =
document.querySelector(`[id ^= "MediaGallery-${this.dataset.section}"]`)
if (mediaGallery.hasAttribute("media-grouping-enabled")) {
mediaGallery.querySelectorAll('[data-media-group]')
.forEach(el = > el.classList.add('hide-media'))
variantValues
.forEach(
value =
> {mediaGallery
.querySelectorAll(`[data - media - group = "${value}"]`)
.forEach(el = > el.classList.remove('hide-media'))})
mediaGallery.querySelectorAll('slider-component')
.forEach(slider = > {slider.initPages()})
}
attaching a screenshot so that you placed the code correctly
now replaced the code in product-media-gallery.liquid with the below code
{%- liquid
if section.settings.hide_variants and variant_images.size == product.media.size
assign single_media_visible = true
endif
assign media_count = product.media.size
if section.settings.hide_variants and media_count > 1 and variant_images.size > 0
assign media_count = media_count | minus: variant_images.size | plus: 1
endif
if media_count == 1 or single_media_visible
assign single_media_visible_mobile = true
endif
if media_count == 0 or single_media_visible_mobile or section.settings.mobile_thumbnails == 'show' or section.settings.mobile_thumbnails == 'columns' and media_count < 3
assign hide_mobile_slider = true
endif
if section.settings.media_size == 'large'
assign media_width = 0.65
elsif section.settings.media_size == 'medium'
assign media_width = 0.55
elsif section.settings.media_size == 'small'
assign media_width = 0.45
endif
assign id_append = ''
if is_duplicate
assign id_append = '-duplicate'
endif
-%}
{%- liquid
if section.settings.enable_group_media
assign optionNames = section.settings.media_grouping_option | split:","
assign option_name_present = false
assign image_positions = ""
assign group_values = ""
for option in optionNames
for product_option in product.options
if option == product_option
assign option_index = forloop.index0
assign option_name_present = true
break
endif
endfor
endfor
if option_name_present
for variant in product.variants
assign group_values_arr = group_values | split:","
assign value_already_present = false
for value in group_values_arr
if value == variant.options[option_index]
assign value_already_present = true
break
endif
endfor
unless value_already_present
assign group_values = group_values | append: variant.options[option_index] | append:","
assign image_positions = image_positions| append: variant.featured_media.position| append:","
endunless
endfor
assign image_positions = image_positions | split: ','
assign group_values = group_values | split: ','
assign group_values_size = image_positions.size | minus : 1
assign data_group_arr = ""
for media in product.media
for i in (0..group_values_size)
if i == 0
assign start_position = 1
else
assign start_position = image_positions[i] | plus: 0
endif
assign end_index = i | plus: 1
assign end_position = image_positions[end_index] | default:9999 | plus: 0
if media.position >= start_position and media.position < end_position
assign data_group_arr = data_group_arr | append: group_values[i] | append: ","
endif
endfor
endfor
assign data_group_arr = data_group_arr | split:","
endif
endif
-%}
<style>
.hide-media{
display:none;
width:0
}
</style>
<media-gallery
{% if option_name_present %}
media-grouping-enabled
{% endif %}
id="MediaGallery-{{ section.id }}{{ id_append }}"
role="region"
{% if section.settings.enable_sticky_info %}
class="product__column-sticky"
{% endif %}
aria-label="{{ 'products.product.media.gallery_viewer' | t }}"
data-desktop-layout="{{ section.settings.gallery_layout }}"
>
<div id="GalleryStatus-{{ section.id }}" class="visually-hidden" role="status"></div>
<slider-component id="GalleryViewer-{{ section.id }}{{ id_append }}" class="slider-mobile-gutter">
{%- unless is_duplicate -%}
<a class="skip-to-content-link button visually-hidden quick-add-hidden" href="#ProductInfo-{{ section.id }}">
{{ 'accessibility.skip_to_product_info' | t }}
</a>
{%- endunless -%}
<ul
id="Slider-Gallery-{{ section.id }}{{ id_append }}"
class="product__media-list contains-media grid grid--peek list-unstyled slider slider--mobile"
role="list"
>
{%- if product.selected_or_first_available_variant.featured_media != null -%}
{%- assign featured_media = product.selected_or_first_available_variant.featured_media -%}
<li
data-media-group="{{ product.selected_or_first_available_variant.options[option_index] }}"
id="Slide-{{ section.id }}-{{ featured_media.id }}{{ id_append }}"
class="product__media-item grid__item slider__slide is-active{% if single_media_visible %} product__media-item--single{% endif %}{% if featured_media.media_type != 'image' %} product__media-item--full{% endif %}{% if section.settings.hide_variants and variant_images contains featured_media.src %} product__media-item--variant{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--fade-in{% endif %}"
data-media-id="{{ section.id }}-{{ featured_media.id }}"
>
{%- assign media_position = 1 -%}
{% render 'product-thumbnail',
media: featured_media,
media_count: media_count,
position: media_position,
desktop_layout: section.settings.gallery_layout,
mobile_layout: section.settings.mobile_thumbnails,
loop: section.settings.enable_video_looping,
modal_id: section.id,
xr_button: true,
media_width: media_width,
media_fit: section.settings.media_fit,
constrain_to_viewport: section.settings.constrain_to_viewport,
lazy_load: false
%}
</li>
{%- endif -%}
{%- for media in product.media -%}
{%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
<li
data-media-group="{{data_group_arr[forloop.index0]}}"
id="Slide-{{ section.id }}-{{ media.id }}{{ id_append }}"
class="{% if product.selected_or_first_available_variant.options[option_index] != data_group_arr[forloop.index0] and section.settings.enable_group_media %}hide-media {% endif %} product__media-item grid__item slider__slide{% if single_media_visible %} product__media-item--single{% endif %}{% if product.selected_or_first_available_variant.featured_media == null and forloop.index == 1 %} is-active{% endif %}{% if media.media_type != 'image' %} product__media-item--full{% endif %}{% if section.settings.hide_variants and variant_images contains media.src %} product__media-item--variant{% endif %}{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--fade-in{% endif %}"
data-media-id="{{ section.id }}-{{ media.id }}"
>
{%- liquid
assign media_position = media_position | default: 0 | plus: 1
assign lazy_load = false
if media_position > 1
assign lazy_load = true
endif
-%}
{% render 'product-thumbnail',
media: media,
media_count: media_count,
position: media_position,
desktop_layout: section.settings.gallery_layout,
mobile_layout: section.settings.mobile_thumbnails,
loop: section.settings.enable_video_looping,
modal_id: section.id,
xr_button: true,
media_width: media_width,
media_fit: section.settings.media_fit,
constrain_to_viewport: section.settings.constrain_to_viewport,
lazy_load: lazy_load
%}
</li>
{%- endunless -%}
{%- endfor -%}
</ul>
{%- unless is_duplicate -%}
<div class="slider-buttons no-js-hidden quick-add-hidden{% if hide_mobile_slider %} small-hide{% endif %}">
<button
type="button"
class="slider-button slider-button--prev"
name="previous"
aria-label="{{ 'general.slider.previous_slide' | t }}"
>
{{- 'icon-caret.svg' | inline_asset_content -}}
</button>
<div class="slider-counter caption">
<span class="slider-counter--current">1</span>
<span aria-hidden="true"> / </span>
<span class="visually-hidden">{{ 'general.slider.of' | t }}</span>
<span class="slider-counter--total">{{ media_count }}</span>
</div>
<button
type="button"
class="slider-button slider-button--next"
name="next"
aria-label="{{ 'general.slider.next_slide' | t }}"
>
{{- 'icon-caret.svg' | inline_asset_content -}}
</button>
</div>
{%- endunless -%}
</slider-component>
{%- if first_3d_model -%}
<button
class="button button--full-width product__xr-button"
type="button"
aria-label="{{ 'products.product.xr_button_label' | t }}"
data-shopify-xr
data-shopify-model3d-id="{{ first_3d_model.id }}"
data-shopify-title="{{ product.title | escape }}"
data-shopify-xr-hidden
>
{% render 'icon-3d-model' %}
{{ 'products.product.xr_button' | t }}
</button>
{%- endif -%}
{%- if media_count > 1
and section.settings.gallery_layout contains 'thumbnail'
or section.settings.mobile_thumbnails == 'show'
-%}
<slider-component
id="GalleryThumbnails-{{ section.id }}{{ id_append }}"
class="thumbnail-slider slider-mobile-gutter quick-add-hidden{% unless section.settings.gallery_layout contains 'thumbnail' %} medium-hide large-up-hide{% endunless %}{% if section.settings.mobile_thumbnails != 'show' %} small-hide{% endif %}{% if media_count <= 3 %} thumbnail-slider--no-slide{% endif %}"
>
<button
type="button"
class="slider-button slider-button--prev{% if media_count <= 3 %} small-hide{% endif %}{% if media_count <= 4 %} medium-hide large-up-hide{% endif %}"
name="previous"
aria-label="{{ 'general.slider.previous_slide' | t }}"
aria-controls="GalleryThumbnails-{{ section.id }}"
data-step="3"
>
{{- 'icon-caret.svg' | inline_asset_content -}}
</button>
<ul
id="Slider-Thumbnails-{{ section.id }}{{ id_append }}"
class="thumbnail-list list-unstyled slider slider--mobile{% if section.settings.gallery_layout == 'thumbnail_slider' %} slider--tablet-up{% endif %}"
>
{%- capture sizes -%}
(min-width: {{ settings.page_width }}px) calc(({{ settings.page_width | minus: 100 | times: media_width | round }} - 4rem) / 4),
(min-width: 990px) calc(({{ media_width | times: 100 }}vw - 4rem) / 4),
(min-width: 750px) calc((100vw - 15rem) / 8),
calc((100vw - 8rem) / 3)
{%- endcapture -%}
{%- if featured_media != null -%}
{%- liquid
capture media_index
if featured_media.media_type == 'model'
increment model_index
elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video'
increment video_index
elsif featured_media.media_type == 'image'
increment image_index
endif
endcapture
assign media_index = media_index | plus: 1
-%}
<li
data-media-group="{{ product.selected_or_first_available_variant.options[option_index] }}"
id="Slide-Thumbnails-{{ section.id }}-0{{ id_append }}"
class="thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains featured_media.src %} thumbnail-list_item--variant{% endif %}"
data-target="{{ section.id }}-{{ featured_media.id }}"
data-media-position="{{ media_index }}"
>
{%- capture thumbnail_id -%}
Thumbnail-{{ section.id }}-0{{ id_append }}
{%- endcapture -%}
<button
class="thumbnail global-media-settings global-media-settings--no-shadow"
aria-label="{%- if featured_media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif featured_media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif featured_media.media_type == 'video' or featured_media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}"
aria-current="true"
aria-controls="GalleryViewer-{{ section.id }}{{ id_append }}"
aria-describedby="{{ thumbnail_id }}"
>
{{
featured_media.preview_image
| image_url: width: 416
| image_tag:
loading: 'lazy',
sizes: sizes,
widths: '54, 74, 104, 162, 208, 324, 416',
id: thumbnail_id,
alt: featured_media.alt
| escape
}}
</button>
</li>
{%- endif -%}
{%- for media in product.media -%}
{%- unless media.id == product.selected_or_first_available_variant.featured_media.id -%}
{%- liquid
capture media_index
if media.media_type == 'model'
increment model_index
elsif media.media_type == 'video' or media.media_type == 'external_video'
increment video_index
elsif media.media_type == 'image'
increment image_index
endif
endcapture
assign media_index = media_index | plus: 1
-%}
<li
data-media-group="{{data_group_arr[forloop.index0]}}"
id="Slide-Thumbnails-{{ section.id }}-{{ forloop.index }}{{ id_append }}"
class="{% if product.selected_or_first_available_variant.options[option_index] != data_group_arr[forloop.index0] and section.settings.enable_group_media %}hide-media {% endif %} thumbnail-list__item slider__slide{% if section.settings.hide_variants and variant_images contains media.src %} thumbnail-list_item--variant{% endif %}"
data-target="{{ section.id }}-{{ media.id }}"
data-media-position="{{ media_index }}"
>
{%- if media.media_type == 'model' -%}
<span class="thumbnail__badge" aria-hidden="true">
{%- render 'icon-3d-model' -%}
</span>
{%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}
<span class="thumbnail__badge" aria-hidden="true">
{%- render 'icon-play' -%}
</span>
{%- endif -%}
{%- capture thumbnail_id -%}
Thumbnail-{{ section.id }}-{{ forloop.index }}{{ id_append }}
{%- endcapture -%}
<button
class="thumbnail global-media-settings global-media-settings--no-shadow"
aria-label="{%- if media.media_type == 'image' -%}{{ 'products.product.media.load_image' | t: index: media_index }}{%- elsif media.media_type == 'model' -%}{{ 'products.product.media.load_model' | t: index: media_index }}{%- elsif media.media_type == 'video' or media.media_type == 'external_video' -%}{{ 'products.product.media.load_video' | t: index: media_index }}{%- endif -%}"
{% if media == product.selected_or_first_available_variant.featured_media
or product.selected_or_first_available_variant.featured_media == null
and forloop.index == 1
%}
aria-current="true"
{% endif %}
aria-controls="GalleryViewer-{{ section.id }}{{ id_append }}"
aria-describedby="{{ thumbnail_id }}"
>
{{
media.preview_image
| image_url: width: 416
| image_tag:
loading: 'lazy',
sizes: sizes,
widths: '54, 74, 104, 162, 208, 324, 416',
id: thumbnail_id,
alt: media.alt
| escape
}}
</button>
</li>
{%- endunless -%}
{%- endfor -%}
</ul>
<button
type="button"
class="slider-button slider-button--next{% if media_count <= 3 %} small-hide{% endif %}{% if media_count <= 4 %} medium-hide large-up-hide{% endif %}"
name="next"
aria-label="{{ 'general.slider.next_slide' | t }}"
aria-controls="GalleryThumbnails-{{ section.id }}"
data-step="3"
>
{{- 'icon-caret.svg' | inline_asset_content -}}
</button>
</slider-component>
{%- endif -%}
</media-gallery>
now make sure your images has desire alt tags use in that with respective name like
green, red etc and it should be arranged in that format too
make sure In the customise Section you need to enable the variant image settings:
you will get your desire output
Hi @LEGACY3 ![]()
A setup like this can be achieved quite smoothly with Easify Product Options ![]()
When customers select a color variant such as Pink, the product gallery can automatically switch to the full set of images corresponding to that specific color.
First, you can create and upload images for each color directly in Shopify as usual. Each color should be created as a separate product so it can have its own dedicated image gallery.
Then, I’ve created a similar demo to help you better visualize how it works:
Then inside the app, if you’d like to add Size options, you can simply create another option using the Buttons or Dropdown display type and add sizes such as S, M, L, XL, etc.
Next, to make the gallery automatically switch based on the selected color, you can use the Add/Edit Cross-product links feature inside the Color Swatches option.
There, you only need to paste the corresponding product URL for each color. Once everything is set up, when customers click on the Black swatch, they’ll automatically be redirected to the Black product page along with its complete dedicated image gallery.
This setup not only creates a much cleaner and more professional shopping experience, but is also especially useful for stores that have multiple mockups, lifestyle images, or separate galleries for each color. Everything can be configured directly inside the app without needing to edit code or make complex theme customizations. ![]()
Shopify allows you to assign one image directly to each variant. If you need multiple images per variant, there are generally two approaches:
Use image alt tags to group variant images. This method works, but it is generally not recommended because it can make image alt text less useful for accessibility and SEO.
Use a Shopify app designed for variant image galleries. This is usually the preferred option, especially if you have many products or need support for additional media types such as videos and 3D models.
Few apps listed below: