How to Show Multiple Images Per Variant in Shopify without apps
Are you trying to figure out how to assign multiple images to one variant on your Shopify store? This guide provides a complete solution for showing multiple images per variant, creating dynamic variant image groups that switch when a customer makes a selection.
If you’ve been asking yourself, “How to show different images when I select a color on Shopify?” or wondering why you’re “only showing one image for variants,” you’ve come to the right place. We’ll fix the issue where the product gallery isn’t updating for variants and enable you to change all product photos when a variant changes.
## Step 1: Duplicate Your Theme (Safety First!)
Before making any code changes, always create a backup copy of your theme.
- From your Shopify Admin, go to Online Store > Themes.
- Find your Horizon theme, click the three-dots icon (…), and select Duplicate.
- All of our work will be done on this new duplicated theme.
## Step 2: Find the File and Change the Code
Now we will find the correct theme file and replace its content.
- In your Shopify Admin, go to Online Store > Themes.
- Find your duplicated theme, click the three-dots icon (…), and select Edit code.
- In the file list on the left, open the folder named blocks. Inside this folder, you will find the file we need to edit.
- Click on the file named
_product-media-gallery.liquidto open it. - Delete all of the code currently in the
_product-media-gallery.liquidfile. - Copy and paste the following complete code into the now-empty file:
<!--
The contents of this file are auto-generated by the Dawn theme editor.
Any changes made to this file will be overwritten.
To make changes to this file, please use the theme editor.
-->
{% # import schema from '../schemas/blocks/_product-media-gallery.js' %}
{%- if block.settings.zoom -%}
<script
src="{{ 'zoom-dialog.js' | asset_url }}"
type="module"
></script>
{%- endif -%}
{%- liquid
assign selected_product = closest.product
assign selected_variant_media = selected_product.selected_or_first_available_variant.featured_media
assign first_3d_model = selected_product.media | where: 'media_type', 'model' | first
if block.settings.hide_variants
assign variant_images = product.images | where: 'attached_to_variant?', true | map: 'src'
endif
if block.settings.slideshow_controls_style == 'thumbnails'
assign controls_on_media = false
else
assign controls_on_media = true
endif
assign render_slideshow_controls = false
if block.settings.media_presentation == 'carousel' or block.settings.slideshow_controls_style == block.settings.slideshow_mobile_controls_style
assign render_slideshow_controls = true
endif
assign render_mobile_slideshow_controls = false
if block.settings.slideshow_controls_style != block.settings.slideshow_mobile_controls_style
assign slideshow_controls_class = 'mobile:hidden'
if block.settings.slideshow_mobile_controls_style != 'hint'
assign render_mobile_slideshow_controls = true
endif
endif
assign slideshow_controls_style = block.settings.slideshow_controls_style
if slideshow_controls_style == 'dots' and selected_product.media.size > 15
assign slideshow_controls_style = 'counter'
endif
assign render_slideshow_arrows = false
if selected_product.media.size <= 1
assign slideshow_class = 'product-media-gallery__slideshow--single-media slideshow--single-media'
else
assign slideshow_class = ''
if block.settings.media_presentation == 'carousel'
assign render_slideshow_arrows = true
endif
endif
if slideshow_controls_style == 'thumbnails'
assign pagination_position = block.settings.thumbnail_position
else
assign pagination_position = 'center'
endif
if pagination_position == 'bottom'
assign pagination_position = 'center'
endif
assign icons_position = 'center'
if block.settings.slideshow_controls_position == 'on_media'
if pagination_position == 'left'
assign icons_position = 'right'
elsif pagination_position == 'right'
assign icons_position = 'left'
endif
endif
-%}
{%- liquid
if block.settings.hide_variants == false
assign selected_variant_media = product.selected_or_first_available_variant.featured_media
if selected_variant_media
assign sorted_media = '' | split: ',' | concat: product.media | where: 'id', selected_variant_media.id
assign remaining_media = product.media | where: 'id', '!=', selected_variant_media.id
assign sorted_media = sorted_media | concat: remaining_media
else
assign sorted_media = product.media
endif
else
assign selected_variant = product.selected_or_first_available_variant
assign filter_option_value = selected_variant.options.first | handleize
assign target_alt = '#' | append: filter_option_value
assign variant_specific_media = product.media | where: 'alt', target_alt
if variant_specific_media.size > 0
assign sorted_media = variant_specific_media
else
assign sorted_media = product.media | where: 'id', selected_variant.featured_media.id
endif
endif
assign has_image_drop = sorted_media | has: 'media_type', 'image'
-%}
{%- if has_image_drop -%}
<script
src="{{ 'drag-zoom-wrapper.js' | asset_url }}"
type="module"
></script>
{%- endif -%}
{% style %}
{% unless block.settings.aspect_ratio == 'adapt' %}
.product-media-container {
--media-preview-ratio: {{ block.settings.aspect_ratio }};
}
{% endunless %}
{% endstyle %}
<media-gallery
class="
spacing-style
sticky-content
{% if block.settings.media_presentation == 'grid' %}
media-gallery--{{ block.settings.media_columns }}-column
{% if block.settings.media_columns == "two" and block.settings.large_first_image%}media-gallery--large-first-image{% endif %}
{% endif %}
media-gallery--{{ block.settings.media_presentation }}
{% if block.settings.slideshow_mobile_controls_style == 'hint' %}
media-gallery--hint
{% endif %}
{% if block.settings.extend_media %}
media-gallery--extend
{% endif %}
"
style="{% render 'spacing-style', settings: block.settings %} --thumbnail-width: {{ block.settings.thumbnail_width }}px; --media-radius: {{ block.settings.media_radius }}px;{% if block.settings.icons_style contains 'large' %} --slideshow-icon-padding: 0;{% endif %}--image-gap: {{ block.settings.image_gap }}px;"
data-presentation="{{ block.settings.media_presentation }}"
{{ block.shopify_attributes }}
>
{% capture slides %}
{% for media in sorted_media %}
{% capture children %}
{%- render 'product-media', media: media -%}
{% endcapture %}
{% capture class %}
product-media-container product-media-container--{{ media.media_type }}{% if block.settings.constrain_to_viewport %} constrain-height{% endif %}{% if block.settings.aspect_ratio != 'adapt' %} media-fit{% endif %}{% if block.settings.zoom %} product-media-container--zoomable{% endif %}
{% endcapture %}
{% if block.settings.aspect_ratio == 'adapt' %}
{% capture style %}
--media-preview-ratio: {{ media.preview_image.aspect_ratio | default: 1.0 }};
{% endcapture %}
{% endif %}
{% if block.settings.zoom and media.media_type == 'model' %}
{%- capture attributes -%}"{% if settings.transition_to_main_product and forloop.first %} data-view-transition-type="product-image-transition"{% endif %}"{% endcapture -%}
{% elsif block.settings.zoom %}
{%- capture attributes -%}on:click="#zoom-dialog-{{ block.id }}/open/{{ forloop.index0 }}"{% if settings.transition_to_main_product and forloop.first %} data-view-transition-type="product-image-transition"{% endif %}{% endcapture -%}
{% endif %}
{% render 'slideshow-slide',
index: forloop.index,
children: children,
class: class,
style: style,
attributes: attributes,
%}
{% endfor %}
{% endcapture %}
{% if sorted_media.size > 1 %}
{% capture controls %}
{% if render_slideshow_controls %}
{%- render 'slideshow-controls',
class: slideshow_controls_class,
style: slideshow_controls_style,
item_count: sorted_media.size,
thumbnails: sorted_media,
controls_on_media: controls_on_media,
pagination_position: pagination_position,
aspect_ratio: block.settings.aspect_ratio
-%}
{% endif %}
{% if render_mobile_slideshow_controls %}
{%- render 'slideshow-controls',
class: 'desktop:hidden media-gallery__mobile-controls',
style: block.settings.slideshow_mobile_controls_style,
item_count: sorted_media.size,
controls_on_media: true,
pagination_position: 'center',
-%}
{% endif %}
{% endcapture %}
{% endif %}
{% if render_slideshow_arrows %}
{% capture slideshow_arrows %}
{% render 'slideshow-arrows', class: 'mobile:hidden', icon_style: block.settings.icons_style, icon_shape: settings.icons_shape %}
{% endcapture %}
{% endif %}
{% render 'slideshow',
ref: 'slideshow',
class: slideshow_class,
slides: slides,
slide_count: sorted_media.size,
slideshow_arrows: slideshow_arrows,
arrows_position: icons_position,
controls: controls
%}
{% if block.settings.media_presentation == 'grid' %}
<ul
class="media-gallery__grid list-unstyled"
data-testid="media-gallery-grid"
>
{% for media in sorted_media %}
<li
ref="media[]"
class="
product-media-container
product-media-container--{{ media.media_type }}
{%- if block.settings.constrain_to_viewport %} constrain-height{% endif %}
{%- if block.settings.aspect_ratio != 'adapt' %} media-fit{% endif %}
"
style="{% if block.settings.aspect_ratio == 'adapt' %} --media-preview-ratio: {{ media.preview_image.aspect_ratio | default: 1.0 }};{% endif %}"
{% if settings.transition_to_main_product and forloop.first %}
data-view-transition-type="product-image-transition"
{% endif %}
>
{%- if block.settings.zoom and media.media_type == 'image' -%}
<button
type="button"
class="button button-unstyled product-media-container__zoom-button"
aria-label="{{ 'actions.zoom' | t }}"
on:click="/zoom/{{ forloop.index0 }}"
>
<span class="visually-hidden">{{ 'actions.open_image_in_full_screen' | t }}</span>
</button>
{%- endif -%}
{%- render 'product-media', media: media -%}
</li>
{% endfor %}
</ul>
{% endif %}
{%- if block.settings.zoom -%}
<zoom-dialog
ref="zoomDialogComponent"
id="zoom-dialog-{{ block.id }}"
>
<dialog
ref="dialog"
on:keydown="/handleKeyDown"
scroll-lock
>
<button
type="button"
class="button button-unstyled close-button dialog-zoomed-gallery__close-button"
aria-label="{{ 'actions.close' | t }}"
on:click="/close"
>
<span class="visually-hidden">{{ 'actions.close' | t }}</span>
{{ 'icon-close.svg' | inline_asset_content }}
</button>
<div class="dialog-thumbnails-list-container">
<scroll-hint
class="dialog-thumbnails-list list-unstyled"
ref="thumbnails"
>
{% if sorted_media.size > 1 %}
{%- for media in sorted_media -%}
{% liquid
assign aspect_ratio = block.settings.aspect_ratio
if block.settings.aspect_ratio == 'adapt'
assign aspect_ratio = media.preview_image.aspect_ratio | default: 1.0
endif
%}
<button
type="button"
class="button button-unstyled dialog-thumbnails-list__thumbnail"
aria-label="{{ 'accessibility.scroll_to' | t: title: media.alt | default: media.media_type }}"
on:click="/handleThumbnailClick/{{ forloop.index0 }}"
on:pointerenter="/handleThumbnailPointerEnter/{{ forloop.index0 }}"
style="--aspect-ratio: {{ aspect_ratio }}; --gallery-aspect-ratio: {{ aspect_ratio }};"
{% if forloop.first %}
aria-selected="true"
{% endif %}
>
{{
media.preview_image
| image_url: width: 1024
| image_tag:
loading: 'lazy',
sizes: 'auto, 110, (min-width: 750px) 160',
widths: '300, 375, 450, 525, 600, 675, 750, 768, 850, 900, 1024',
alt: media.alt
| escape
}}
</button>
{%- endfor -%}
{% endif %}
</scroll-hint>
</div>
<ul
class="dialog-zoomed-gallery list-unstyled"
>
{%- for media in sorted_media -%}
<li
id="product-{{ media.id}}-{{ forloop.index }}"
class="
product-media-container
product-media-container--{{ media.media_type }}
{% if block.settings.constrain_to_viewport %} constrain-height{% endif %}
{% if block.settings.aspect_ratio != 'adapt' %} media-fit{% endif %}
{% if media.media_type == 'image' %} product-media-container--zoomable{% endif %}
"
style="{% if block.settings.aspect_ratio == 'adapt' %} --media-preview-ratio: {{ media.preview_image.aspect_ratio | default: 1.0 }};{% endif %}"
ref="media[]"
{% if media.media_type == 'image' %}
on:click="/close"
{% endif %}
>
{% if media.media_type == 'image' %}
<drag-zoom-wrapper class="product-media__drag-zoom-wrapper">
{%- render 'product-media', media: media -%}
</drag-zoom-wrapper>
{% else %}
{%- render 'product-media', media: media -%}
{% endif %}
</li>
{%- endfor -%}
</ul>
</dialog>
</zoom-dialog>
{%- endif -%}
{%- if first_3d_model -%}
<link
id="ModelViewerStyle"
rel="stylesheet"
href="https://cdn.shopify.com/shopifycloud/model-viewer-ui/assets/v1.0/model-viewer-ui.css"
media="print"
onload="this.media='all'"
>
<script>
window.ProductModel = {
loadShopifyXR() {
Shopify.loadFeatures([
{
name: 'shopify-xr',
version: '1.0',
onLoad: this.setupShopifyXR.bind(this),
},
]);
},
setupShopifyXR(errors) {
if (errors) return;
if (!window.ShopifyXR) {
document.addEventListener('shopify_xr_initialized', () => this.setupShopifyXR());
return;
}
document.querySelectorAll('[id^="ProductModelJSON-"]').forEach((modelJSON) => {
window.ShopifyXR.addModels(JSON.parse(modelJSON.textContent));
modelJSON.remove();
});
window.ShopifyXR.setupXRElements();
},
};
window.ProductModel.loadShopifyXR();
</script>
{%- endif -%}
</media-gallery>
{% stylesheet %}
.dialog-zoomed-gallery {
cursor: zoom-out;
}
.dialog--preloading {
opacity: 0;
}
.product-media__drag-zoom-wrapper {
aspect-ratio: inherit;
min-height: inherit;
min-width: inherit;
display: inherit;
flex: inherit;
}
@media screen and (max-width: 749px) {
.dialog-zoomed-gallery {
/* Prevent scroll wheel or swipe scrolling */
overscroll-behavior: none;
scrollbar-width: none;
display: flex;
scroll-snap-type: x mandatory;
overflow-x: hidden;
scroll-behavior: smooth;
height: 100%;
&::-webkit-scrollbar {
display: none;
}
}
.dialog-zoomed-gallery .product-media-container {
flex: 0 0 100%;
scroll-snap-align: start;
position: relative;
}
.dialog-zoomed-gallery .product-media-container--image .product-media {
aspect-ratio: auto;
height: 100%;
width: 100%;
overflow: hidden;
}
.dialog-zoomed-gallery .product-media-container--video,
.dialog-zoomed-gallery .product-media-container--external_video {
align-content: center;
}
.dialog-zoomed-gallery
:is(.product-media-container--video, .product-media-container--external_video, .product-media-container--model)
.product-media {
aspect-ratio: auto;
align-items: center;
height: 100%;
.product-media__image {
height: 100%;
}
}
.product-media__drag-zoom-wrapper {
display: flex;
aspect-ratio: auto;
height: 100%;
width: 100%;
overflow: scroll;
scrollbar-width: none;
justify-content: center;
&::-webkit-scrollbar {
display: none;
}
}
.product-media__drag-zoom-wrapper .product-media__image {
--product-media-fit: contain;
overflow: hidden;
transform: scale(var(--drag-zoom-scale))
translate(var(--drag-zoom-translate-x, 0), var(--drag-zoom-translate-y, 0));
}
.media-gallery--hint {
--slideshow-gap: var(--gap-2xs);
:not(.dialog-zoomed-gallery) > .product-media-container:not(:only-child) {
width: 90%;
.product-media img {
object-fit: cover;
}
}
}
}
.dialog-zoomed-gallery__close-button {
border-radius: 50%;
color: white;
mix-blend-mode: difference;
z-index: var(--layer-raised);
}
.media-gallery__mobile-controls {
grid-area: auto;
}
.dialog-zoomed-gallery .product-media-container--zoomable.product-media-container--image {
cursor: zoom-out;
}
.product-media-container--zoomable.product-media-container--image {
cursor: zoom-in;
}
.dialog-zoomed-gallery .product-media-container--video deferred-media,
.dialog-zoomed-gallery .product-media-container--external_video deferred-media {
height: auto;
aspect-ratio: var(--ratio);
}
.dialog-zoomed-gallery .product-media-container--model .product-media__image {
/* Make the height match the height of the model-viewer */
height: 100vh;
}
{% endstylesheet %}
{% schema %}
{
"name": "t:names.product_media",
"tag": null,
"settings": [
{
"type": "select",
"id": "media_presentation",
"label": "t:settings.type",
"options": [
{
"value": "grid",
"label": "t:options.grid"
},
{
"value": "carousel",
"label": "t:options.carousel"
}
],
"default": "grid",
"info": "t:info.carousel_layout_on_mobile"
},
{
"type": "header",
"content": "t:content.grid",
"visible_if": "{{ block.settings.media_presentation == 'grid' }}"
},
{
"type": "select",
"id": "media_columns",
"label": "t:settings.columns",
"options": [
{
"value": "one",
"label": "t:options.one_number"
},
{
"value": "two",
"label": "t:options.two_number"
}
],
"default": "one",
"visible_if": "{{ block.settings.media_presentation == 'grid' }}"
},
{
"type": "range",
"id": "image_gap",
"label": "t:settings.gap",
"min": 0,
"max": 100,
"unit": "px",
"step": 1,
"default": 0,
"visible_if": "{{ block.settings.media_presentation == 'grid' }}"
},
{
"type": "checkbox",
"id": "large_first_image",
"label": "t:settings.full_width_first_image",
"default": false,
"visible_if": "{{ block.settings.media_presentation == 'grid' and block.settings.media_columns == 'two' }}"
},
{
"type": "header",
"content": "t:content.carousel"
},
{
"type": "select",
"id": "icons_style",
"label": "t:settings.icons",
"options": [
{
"value": "arrow",
"label": "t:options.arrows"
},
{
"value": "chevron",
"label": "t:options.chevrons"
},
{
"value": "arrows_large",
"label": "t:options.large_arrows"
},
{
"value": "chevron_large",
"label": "t:options.large_chevrons"
}
],
"default": "arrow",
"visible_if": "{{ block.settings.media_presentation == 'carousel' }}"
},
{
"type": "select",
"id": "slideshow_controls_style",
"label": "t:settings.pagination",
"options": [
{
"value": "dots",
"label": "t:options.dots"
},
{
"value": "counter",
"label": "t:options.counter"
},
{
"value": "thumbnails",
"label": "t:options.thumbnails"
}
],
"visible_if": "{{ block.settings.media_presentation == 'carousel' }}"
},
{
"type": "select",
"id": "slideshow_mobile_controls_style",
"label": "t:settings.mobile_pagination",
"options": [
{
"value": "dots",
"label": "t:options.dots"
},
{
"value": "counter",
"label": "t:options.counter"
},
{
"value": "hint",
"label": "t:options.hint"
}
]
},
{
"type": "header",
"content": "t:content.thumbnails",
"visible_if": "{{ block.settings.media_presentation == 'carousel' and block.settings.slideshow_controls_style == 'thumbnails' }}"
},
{
"type": "select",
"id": "thumbnail_position",
"label": "t:settings.position",
"options": [
{
"value": "left",
"label": "t:options.left"
},
{
"value": "bottom",
"label": "t:options.bottom"
},
{
"value": "right",
"label": "t:options.right"
}
],
"default": "bottom",
"visible_if": "{{ block.settings.media_presentation == 'carousel' and block.settings.slideshow_controls_style == 'thumbnails' }}"
},
{
"type": "range",
"id": "thumbnail_width",
"label": "t:settings.width",
"min": 44,
"max": 72,
"step": 1,
"unit": "px",
"default": 64,
"visible_if": "{{ block.settings.media_presentation == 'carousel' and block.settings.slideshow_controls_style == 'thumbnails' }}"
},
{
"type": "header",
"content": "t:content.media"
},
{
"type": "select",
"id": "aspect_ratio",
"label": "t:settings.aspect_ratio",
"options": [
{
"value": "adapt",
"label": "t:options.auto"
},
{
"value": "1/1.25",
"label": "t:options.portrait"
},
{
"value": "1",
"label": "t:options.square"
},
{
"value": "2/1",
"label": "t:options.landscape"
}
],
"default": "adapt"
},
{
"type": "range",
"id": "media_radius",
"label": "t:settings.border_radius",
"min": 0,
"max": 32,
"step": 1,
"unit": "px",
"default": 4
},
{
"type": "checkbox",
"id": "extend_media",
"label": "t:settings.extend_media_to_screen_edge",
"default": true,
"visible_if": "{{ section.settings.content_width == 'content-center-aligned' }}"
},
{
"type": "checkbox",
"id": "constrain_to_viewport",
"label": "t:settings.limit_media_to_screen_height",
"default": false
},
{
"type": "checkbox",
"id": "zoom",
"label": "t:settings.enable_zoom",
"default": true
},
{
"type": "checkbox",
"id": "video_loop",
"label": "t:settings.enable_video_looping",
"default": false
},
{
"type": "checkbox",
"id": "hide_variants",
"default": false,
"label": "t:settings.hide_unselected_variant_media"
},
{
"type": "header",
"content": "t:content.padding"
},
{
"type": "range",
"id": "padding-block-start",
"label": "t:settings.top",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 0
},
{
"type": "range",
"id": "padding-block-end",
"label": "t:settings.bottom",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 0
},
{
"type": "range",
"id": "padding-inline-start",
"label": "t:settings.left",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 0
},
{
"type": "range",
"id": "padding-inline-end",
"label": "t:settings.right",
"min": 0,
"max": 100,
"step": 1,
"unit": "px",
"default": 0
}
],
"presets": [
{
"name": "t:names.product_media"
}
]
}
{% endschema %}
## Step 3: Set Your Image Alt Text
This is how you’ll tell Shopify which images belong to which variant. This step is crucial for the code to work.
From your Shopify Admin, go to Products and select the product you want to edit.
In the Media section, click on an image that belongs to a specific variant.
Click “Add alt text”.
In the text box, type a hashtag (
#) followed immediately by the variant’s name. The format must be all lowercase, with spaces replaced by hyphens.For a Blue color variant, the
alttext is:#blueFor a style like Heavy Duty, the
alttext would be:#heavy-duty
Repeat this for all images you want to group with specific variants
## Step 4: Configure the Feature
The final step is to enable our new feature from the theme customizer.
Go to the Theme Customizer (Online Store > Themes > Customize).
Navigate to the product page you’re working on.
On the left-hand sidebar, click on the Product media block.
A settings panel will open on the right. Find the checkbox labeled “Hide unselected variant media” and check it to enable our new logic.
Click Save.
## Enjoy Your New Dynamic Gallery! 🚀
That’s it! You have now solved the problem and implemented a powerful feature. Your product pages will now allow a swatch click to show multiple images, creating the dynamic gallery experience your customers expect.
Shopify multiple images per variant
Shopify assign multiple images to one variant
Shopify variant image groups
Shopify one variant multiple images
- Shopify variant images alt text
Shopify Horizon theme variant images
Horizon theme multiple images per variant
Shopify Horizon theme product gallery
Shopify filter images by alt tag
Shopify Liquid for variant images
Shopify hide unselected variant media
How to show different images when I select a color on Shopify?
Shopify only showing one image for variants
Change all product photos when a variant changes Shopify
Shopify product gallery not updating for variants
Shopify swatch click show multiple images