We build custom Shopify features from $99. Every build has a 30-day guarantee.

Build My Feature →

Shopify Horizon: How to Show Multiple Images Per Color Without an App

Shopify Field Notes ··Johnny Chen Founder & Shopify Developer at CheckoutWorks
Shopify Horizon product galleries comparing all images with color-specific image groups when Black is selected.

Want to show multiple variant images on your Shopify Horizon product page without an app? We built this feature by grouping product photos by color using image alt text and custom theme code. Shoppers see the selected color’s photos, while shared images remain visible.

Try the demo below, then follow how we built it. We’ve included the code and setup steps so you can adapt the implementation to your own theme.

Try the live demo

Switch colors to explore matching image groups. Shared images stay visible across selections.

Explore our live demo →

Color-based image groups on our Shopify Horizon demo.

Why a variant image is not enough

Shopify lets you assign one image to each variant. That changes the variant preview, but it does not assign a whole group of front views, model photos, and other angles to one color.

If even the single variant image does not switch when a shopper selects a variant, start with why a Shopify variant image is not changing. This guide assumes the standard variant image already works.

This customization filters the gallery by the selected Color option. It does not change Shopify’s variant image limit. Horizon’s Hide unselected variant media setting is different: it does not read color names from alt text to create these groups.

Before you start

  • Duplicate your theme and work on the unpublished copy.
  • Use a product with an option named Color or Colour, plus images for each color.
  • Check for apps or custom code that already control the product gallery.

Code reference: the snippets below come from our local Horizon 4.3.0 implementation. Compare your files before editing; this is not a drop-in tutorial for every theme or version. “Without an app” still means making a code change.

1. Group images using descriptive alt text

For our demo, we used Black, Military Green, and Natural, with several photos per color and one shared image. The grouping reads the color names in the alt text, not the file names.

To use the same approach, upload your images to the product and describe each one using its actual color option name. For example, use Military Green when that is the option value, rather than shortening it to “Green.”

Example alt text Image group
Black T-shirt shown from the front Black
Model wearing a Military Green T-shirt Military Green
Natural T-shirt shown from the front Natural
Three blank T-shirts layered in a studio product photo Shared across colors

A shared image must not contain any of the product’s color option names in its alt text. Keep the description meaningful: alt text still describes the image for people who cannot see it. Do not leave it blank just to make an image shared.

Shopify Files showing Natural and Military Green image alt text, plus a shared T-shirt image without a color name.

The first image stays shared; the other images include their color names. Select the screenshot to view it at full size.

Watch for ambiguous words. If one color is Natural, “natural lighting” also matches it. Overlapping names such as Black and Washed Black can match the same image. This approach suits clearly distinguishable color names; it is not a general-purpose tagging system.

2. How we built the gallery behavior

We added a setting to Horizon’s product media block, then added one block of code to the gallery snippet to group images by color. The implementation uses Horizon’s existing variant-selection behavior, so it does not need a separate JavaScript layer.

The two changes below come from that implementation. Your files may differ if you use another theme version or have existing customizations. Keep a copy of the original files and work in your duplicate theme.

You can adapt the changes yourself or use an AI coding assistant to compare your files with our example. Provide both code examples below and the relevant theme files. Ask it to preserve existing customizations, explain where each change belongs, and flag any differences that need a different approach. Do not include passwords, access tokens, or customer data.

Review every change before applying it. AI-generated adjustments still need testing on an unpublished theme copy; they are not a guarantee of compatibility.

Add the Variant image groups checkbox

Open blocks/_product-media-gallery.liquid. In its settings array, find the object with "id": "hide_variants". Insert the following object after that object and before the next setting. Keep the comma after the existing object and the trailing comma shown below.

{
  "type": "checkbox",
  "id": "group_media_by_color_alt",
  "label": "Variant image groups",
  "info": "Match images by Color using ALT text. Images without a Color stay shared.",
  "default": false
},

Add the color grouping code

Open snippets/product-media-gallery-content.liquid. In the Liquid block near the top, find this line:

  assign has_image_drop = sorted_media | has: 'media_type', 'image'

Paste the code below on a new line directly above it. Do not remove or change any existing line. If your file does not contain that line, do not guess at another position.

Code preview — expand below for the complete code to copy.

  # Modified by CheckoutWorks.dev: optional Color grouping by image ALT text.
  # Runs after the upstream sorting above and replaces its result only when the
  # selected Color matches at least one image.
  if block_settings.group_media_by_color_alt
    assign color_option = blank

    for product_option in selected_product.options_with_values
      assign normalized_option_name = product_option.name | strip | downcase
      if normalized_option_name == 'color' or normalized_option_name == 'colour'
        assign color_option = product_option
  …
Show full codeCollapse code
  # Modified by CheckoutWorks.dev: optional Color grouping by image ALT text.
  # Runs after the upstream sorting above and replaces its result only when the
  # selected Color matches at least one image.
  if block_settings.group_media_by_color_alt
    assign color_option = blank

    for product_option in selected_product.options_with_values
      assign normalized_option_name = product_option.name | strip | downcase
      if normalized_option_name == 'color' or normalized_option_name == 'colour'
        assign color_option = product_option
        break
      endif
    endfor

    if color_option != blank and color_option.selected_value != blank
      assign grouped_media = '' | split: ','
      assign selected_color_media_count = 0
      assign normalized_selected_color = color_option.selected_value | strip | downcase | replace: '-', ' ' | replace: '_', ' '

      for media in selected_product.media
        assign normalized_media_alt = media.alt | default: '' | strip | downcase
        assign normalized_media_alt = normalized_media_alt | replace: '-', ' ' | replace: '_', ' ' | replace: ',', ' ' | replace: '.', ' ' | replace: ':', ' ' | replace: ';', ' ' | replace: '/', ' '
        assign searchable_media_alt = ' ' | append: normalized_media_alt | append: ' '
        assign media_matches_any_color = false
        assign media_matches_selected_color = false

        for color_value in color_option.values
          assign normalized_color_value = color_value.name | default: color_value | strip | downcase | replace: '-', ' ' | replace: '_', ' '
          assign color_value_needle = ' ' | append: normalized_color_value | append: ' '

          if searchable_media_alt contains color_value_needle
            assign media_matches_any_color = true
            if normalized_color_value == normalized_selected_color
              assign media_matches_selected_color = true
            endif
          endif
        endfor

        if media_matches_selected_color
          assign selected_color_media_count = selected_color_media_count | plus: 1
        endif

        if media_matches_selected_color or media_matches_any_color == false
          assign found_media = selected_product.media | where: 'id', media.id
          assign grouped_media = grouped_media | concat: found_media
        endif
      endfor

      if selected_color_media_count > 0
        assign sorted_media = '' | split: ','

        if selected_variant_media
          assign sorted_media = grouped_media | where: 'id', selected_variant_media.id
        endif

        if sorted_media.size > 0
          for media in grouped_media
            if media.id != selected_variant_media.id
              assign found_media = grouped_media | where: 'id', media.id
              assign sorted_media = sorted_media | concat: found_media
            endif
          endfor
        else
          assign sorted_media = grouped_media
        endif

        # The upstream values above are based on the full media count; redo them for the group.
        assign slideshow_controls_style = block_settings.slideshow_controls_style
        if slideshow_controls_style == 'dots' and sorted_media.size > 15
          assign slideshow_controls_style = 'counter'
        endif

        if sorted_media.size <= 1
          assign slideshow_class = 'product-media-gallery__slideshow--single-media slideshow--single-media'
          assign render_slideshow_arrows = false
        endif
      endif
    endif
  endif

The code runs after Horizon’s own media sorting and replaces the result only when the selected color matches at least one image. Every original line stays in place, which keeps the change easy to find and reapply after a theme update.

The media list is filtered before Horizon renders the gallery, rather than hiding finished slides with CSS. The gallery, thumbnails, counter, and zoom view can then use the same list. Switching Size does not choose another color group, although a variant’s assigned image can still affect which image appears first.

3. Enable the feature

Preview the product in the theme editor, open its Product media block, and enable Variant image groups. This checkbox appears only after adding the code above.

Custom Variant image groups toggle below Hide unselected variant media in the Shopify Horizon theme editor.

Variant image groups is the custom setting. The native setting above it does not need to be enabled for color grouping to work.

The switch belongs to the media block on the selected template, not to one product record. If only certain products should use it, assign them a dedicated template.

4. Test before publishing

  • Switch colors, then sizes. Check the matching images and shared media.
  • Test mobile swiping, desktop layouts, thumbnails, counters, and zoom.
  • Open a direct variant URL and try unavailable combinations and quick color changes.
  • Disable the setting and confirm the product returns to the original gallery behavior.

This guide focuses on the main product image gallery. Video, 3D media, Quick add, Featured product sections, and Combined Listings need separate checks before you rely on them. Review and retest the customization after a theme update.

If the images do not group as expected

  • All colors appear: check the switch, the Color/Colour option name, and the alt text. If no image matches the selected color, the code falls back to Horizon’s native gallery logic.
  • An image appears everywhere: it may not match any color name, so it is treated as shared. Check spelling and multiword color names.
  • Colors overlap: check for overlapping names or descriptive words such as “natural.” Changing alt text can change grouping.

For a working reference, open the live Horizon demo and compare its color groups with your preview. Keep the duplicate unpublished until the gallery behaves correctly for your products.

Get Shopify help _

Want multiple images for each color?

We can build it into your Shopify store.

$99 USD

COLOR IMAGE GALLERY ·30-DAY GUARANTEE

Group my images by color