Are you struggling with product images showing up for the wrong variants in your Shopify store? Here’s a simple guide to help you display only the relevant images for each product variation, such as different colours of a t-shirt.
Steps by steps instructions
To get the job done, follow each step below
Preparing your images
- Group your product images by colour or variant.
- Ensure the order of these groups matches your variant values (e.g., green, yellow).
- Attach a featured image to each variant.
- Make the featured image for each colour the first in its group.
Installing the necessary code
- Duplicate your theme (ensure it’s a free theme like Dawn).
- Click ‘Edit code’ on the duplicated theme.
- Open the ‘config’ folder and edit the ‘settings_schema.json’ file.
- Paste the required code snippet after the opening bracket and save your changes.
{
"name":"Media grouping option",
"settings":[
{
"type": "text",
"id": "media_grouping_option",
"label": "Media grouping option",
"default": "Color",
"info":"Group variant media together and attach the first image of each group to a variant."
},
{
"type": "paragraph",
"content": "Only one option name at a time, if you need a different option name for specific products please create a new product page template."
},
{
"type": "paragraph",
"content": "If you have a multilingual store, you can enter multiple values separated by commas: Color,Colour,Farbe,Couleur"
}
]
},
Adding a custom liquid block
- Open the theme customiser and go to a product page.
- Add a new custom liquid block in the product information section.
- Paste the second code snippet into the custom liquid settings.
<style>
.hide-media{
display:none;
width:0
}
</style>
<script>
const handle = "products/" + "{{ product.handle }}" + ".js";
fetch(window.Shopify.routes.root + handle)
.then(response => response.json())
.then(product => {
const optionsToWatch = "{{settings.media_grouping_option}}".split(",");
const colorOption = product.options.find(option => optionsToWatch.includes(option.name))|| null;
if (!colorOption) {
console.log("No color options available for this product.");
return;
}
// Retrieve color values
const colorValues = colorOption.values;
// Build an object to store the starting image positions for each color
const colorImagePositions = {};
colorValues.forEach(color => {
// Find the first variant of this color and get its featured image position
const variant = product.variants.find(variant => variant.options[colorOption.position - 1] === color);
if (variant && variant.featured_image) {
colorImagePositions[color] = variant.featured_image.position;
}
});
// Sort colors based on their starting positions to calculate image ranges
const sortedColors = Object.entries(colorImagePositions).sort((a, b) => a[1] - b[1]);
// Calculate ranges and log the result
const colorImageRanges = {};
sortedColors.forEach((color, index) => {
const colorName = color[0];
const startPos = color[1];
const endPos = sortedColors[index + 1] ? sortedColors[index + 1][1] : null;
colorImageRanges[colorName] = endPos ? [startPos, endPos] : [startPos];
});
// Build an object to store image IDs grouped by color based on colorImageRanges
const colorImageIDs = {};
Object.entries(colorImageRanges).forEach(([color, range]) => {
const startPos = range[0];
const endPos = range[1] || product.media.length +1; // If no end, assume range goes to the end of the images
// Find all images within the range for this color
const imageIDs = product.media
.filter((media, index) => {
const imagePosition = index + 1; // Adjust index to be position-based
return imagePosition >= startPos && imagePosition < endPos;
})
.map(media => media.id); // Store the image ID
colorImageIDs[color] = imageIDs;
});
const mediaGallery = document.querySelector('media-gallery');
const productSection = document.querySelector(".product-variant-id")
const sectionID = "{{ section.id }}"
// Iterate over colorImageIDs to apply data-media-group based on the color ranges
Object.entries(colorImageIDs).forEach(([color, imageIDs]) => {
imageIDs.forEach(mediaID => {
// Construct the selector for each media item using sectionID and media ID
const selector = `[data-media-id="${sectionID}-${mediaID}"], [data-target="${sectionID}-${mediaID}"]`;
// Find the image <li> and add the data-media-group attribute with the color
const mediaItems = mediaGallery.querySelectorAll(`li${selector}`);
mediaItems.forEach(mediaItem=>{
mediaItem.setAttribute('data-media-group', color);
})
});
});
const showVariantImage = () => {
const variantSelects = document.querySelector('variant-selects');
// Get the currently selected color
let currentValues = Array.from(
variantSelects.querySelectorAll('select option[selected], fieldset input:checked')
).map(el => el.value);
let selectedColor = currentValues.find(value => colorImageRanges.hasOwnProperty(value));
// Show/Hide images based on selected color
mediaGallery.querySelectorAll('ul li[data-media-group]').forEach((mediaItem, index) => {
if (mediaItem.getAttribute('data-media-group') === selectedColor) {
mediaItem.classList.remove('hide-media');
} else {
mediaItem.classList.add('hide-media');
}
});
// Reinitialize the slider if needed
mediaGallery.querySelectorAll('slider-component').forEach(slider => {
slider.initPages();
});
}
showVariantImage()
// Event listener to show/hide images based on the selected color
productSection.addEventListener('change', showVariantImage);
})
.catch(error => console.error("Error fetching product data:", error));
</script>
Adjusting media grouping settings
- In the customiser, click the gear icon.
- Select ‘Media grouping option’.
- Adjust the option name if needed (e.g., ‘colour’ for UK spelling).
- For multilingual stores, enter multiple values separated by commas.
Troubleshooting
If you can’t see the first gallery thumbnail:
- Click on the product information section.
- Find ‘Hide other variants media after selecting a variant’.
- Enable or disable this option based on your theme version.
Applying to specific products
To use this feature only for certain products:
- Create a new product page template with the custom liquid block.
- Remove the block from the default template.