Horizon

Hello,

Horizon theme - Show only images of selected color variant

I use the Horizon theme on Shopify.

I want customers to see only the images of the selected color variant.

Example:

- If they click Black, only black images should appear.

- If they click White, only white images should appear.

I don’t want to use any paid app.

Is there a free code or solution that works with the Horizon theme?

Thank you.

Did you check the settings? Grid shows a grid, Carousel shows a carousel.

Part of this is already free and built in. In the product admin, open each color variant and assign its image as the variant image; Horizon then swaps the main image automatically when a shopper picks Black or White. What it will not do natively is hide the other color’s thumbnails — that filtering needs either an app or a code edit to the product media gallery. One free first-party option exists: Shopify Combined Listings gives each color its own product and media set, but it requires Plus.

Thanks for the explanation. Is there a free code solution for the Horizon theme that filters the gallery so only the selected variant’s images are shown, without using a paid app or Shopify Plus?

You just assign the variant image to the variant in Admin product page, then select carousel in the media block settings in Editor. Should only show the variant selected.

This shows only the variant image assigned to the variant selected. You can press the arrows to scroll though the images, and that changes the color selection. It is super easy.

If you are looking for some weird theme code edit to have a grid but hide the grid like the last guy, here is the answer to that:

Hi @Firas_Mohamed

Yes, this can be done with custom code. Could you please send me your store URL? I’ll check your theme and then update you with the best solution.

Best regards,
Devcoder :laptop:

I fixed the following and tested on a Horizon store.

Result

  • Pick Black and only the Black images show. Pick White and it swaps to White.
  • Untagged images (size chart) stay visible for every colour.
  • Nothing is hardcoded, so it keeps working when you add or remove images.
  • Works with the grid gallery, which is Horizon’s default product layout.

The reason behind your issue:

Horizon only swaps the one image you assign to a variant. Any other image counts as shared, so it keeps showing for every colour.

There is no native setting that filters a colour’s full set of images. You can do it for free with Alt text plus a small block.

Fix:

Step 1 - tag each image

  • Products > your product > click an image.
  • In Alt text, type the colour that image belongs to (Black, White …).
  • Do this for every colour image. Leave shared images untagged.

Step 2 - add the filter

  • Online Store > Themes > Customize.
  • Open the product template, then Add block > Custom Liquid in the product section.
  • Paste this and Save:
{%- liquid
  assign vf_options = closest.product.options_with_values
-%}
<script>
(function () {
  if (window.__variantMediaFilter) return;
  window.__variantMediaFilter = true;

  var OPTIONS = [
    {%- for o in vf_options -%}
      { "values": [ {%- for v in o.values -%}{%- assign vn = v.name | default: v -%}{{ vn | json }}{%- unless forloop.last -%},{%- endunless -%}{%- endfor -%} ] }{%- unless forloop.last -%},{%- endunless -%}
    {%- endfor -%}
  ];
  if (!OPTIONS.length) return;

  var norm = function (s) { return (s || '').toLowerCase().trim(); };

  function gallerySection() {
    var g = document.querySelector('media-gallery');
    return (g && g.closest('.shopify-section')) || document.body;
  }

  function pickOption(section) {
    var alts = [].map.call(section.querySelectorAll('.product-media img'), function (i) {
      return norm(i.getAttribute('alt'));
    });
    var best = null, bestScore = 0;
    OPTIONS.forEach(function (o) {
      var score = o.values.filter(function (v) {
        var lv = norm(v);
        return lv && alts.some(function (a) { return a.indexOf(lv) > -1; });
      }).length;
      if (score > bestScore) { bestScore = score; best = o; }
    });
    return best;
  }

  function selectedValue(section, values) {
    var vals = values.map(norm);
    var r = [].filter.call(section.querySelectorAll('input:checked'), function (i) {
      return vals.indexOf(norm(i.value)) > -1;
    })[0];
    if (r) return norm(r.value);
    var s = [].filter.call(section.querySelectorAll('select'), function (el) {
      return vals.indexOf(norm(el.value)) > -1;
    })[0];
    return s ? norm(s.value) : null;
  }

  function box(m) { return m.closest('slideshow-slide') || m.closest('li') || m; }

  function apply() {
    var section = gallerySection();
    var opt = pickOption(section);
    if (!opt) return;
    var values = opt.values.map(norm);
    var selected = selectedValue(section, opt.values);
    if (!selected) return;

    var medias = section.querySelectorAll('.product-media');
    var visible = 0;
    [].forEach.call(medias, function (m) {
      var img = m.querySelector('img');
      var alt = norm(img && img.getAttribute('alt'));
      var mentions = values.filter(function (v) { return v && alt.indexOf(v) > -1; });
      var show = mentions.length === 0 || mentions.indexOf(selected) > -1;
      var el = box(m);
      el.style.display = show ? '' : 'none';
      el.hidden = !show;
      if (show) visible++;
    });
    if (!visible) {
      [].forEach.call(medias, function (m) { var el = box(m); el.style.display = ''; el.hidden = false; });
    }
  }

  function boot() {
    var section = gallerySection();
    apply();
    section.addEventListener('change', function () { setTimeout(apply, 60); });
    new MutationObserver(function (muts) {
      for (var i = 0; i < muts.length; i++) {
        if (muts[i].addedNodes && muts[i].addedNodes.length) { apply(); return; }
      }
    }).observe(section, { childList: true, subtree: true });
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
  else boot();
})();
</script>

If one image per colour is enough, you do not need code. Open the Product media block settings and turn on Hide unselected variant media. That hides the other colours’ assigned images, but any unassigned image still shows, which is why the block above exists.

Hi, thank you for sharing this solution. I tried to follow the steps, but I couldn’t get it working. Would you be able to help me implement it on my Horizon theme? I’d really appreciate it. Thank you!

share your store url with password

You can enable this option by going to Edit Theme > Product Template to do that.

Best regards,
Dan from Ryviu Product Reviews & Loyalty app.

Hi @Firas_Mohamed ,
This is actually a native, free feature in Horizon, no custom code needed. It’s just not obvious where the setting lives.

Step 1: Assign images to each color variant (in Admin)

Go to Products → [your product]
Scroll to the Variants section
Click each variant (e.g. “Black”) and assign it one photo this becomes that variant’s “featured image”
Repeat for every color

Step 2: Turn on the hiding option (in Theme Editor)

Go to Online Store → Themes → Customize
Open a product page
Click on the Media Gallery block (or “Product images” block) in the product section
In its settings, find and enable:
“Hide other variants’ media after selecting a variant”
Save

Once both are done, clicking “Black” will show only the Black image (plus any images not assigned to any variant, which always stay visible), and clicking “White” will switch to only the White one.

Quick clarification, since two different things are getting mixed up here :slightly_smiling_face:

One image per color → free, no code. Assign each variant its featured image in the admin, then enable “Hide other variants’ media” in the Media Gallery block settings. Done.

Multiple images per color → native can’t do it. Horizon only swaps the one assigned image per variant; every other image stays shared across all colors. That’s the actual issue here, and no toggle fixes it.

For that case, ajaycodewiz’s alt-text + Custom Liquid solution above is the cleanest free route. Firas — if it’s not filtering, it’s almost always one of three things: alt text not matching your color names exactly, browser cache, or a carousel-vs-grid mismatch. Worth checking those first

Hey @Firas_Mohamed ,

If you’re using the latest version of the Horizon theme, the first thing I’d recommend is checking whether it already supports variant-specific media. If you’ve assigned images to each color variant in the Shopify admin, some versions of Horizon can display the relevant images automatically without additional customization.

If your version doesn’t support that behavior, it can be achieved with custom Liquid and JavaScript by filtering the product gallery based on the selected variant. The good news is that this doesn’t necessarily require a paid app.

That said, there isn’t a single piece of code that works for every Horizon store. The implementation depends on the specific version of the theme and how its product gallery is structured, so any custom solution usually needs to be tailored to the theme’s markup.

If you’re comfortable editing your theme, sharing your Horizon theme version or a link to your product page would make it easier for the community to suggest a solution that fits your store.

If you found my reply helpful, please consider marking it as the accepted solution so it can help other merchants following this discussion.

Thank You !

Hey there @Firas_Mohamed
Yes you can manage that without a paid app. Horizon does support variant media but it depends on the product images that are attached to variants how that will work.

Once you’ve done that, you’ll need to ensure each product image is linked to the correct color variant in Shopify Admin. Then go to theme editor and look for product media settings and enable Filter media by selected variant if available in your version of Horizon.

If you do not have this option, there is a way to add a little liquid and js code to filter the products gallery by the selected variant. Always duplicate the theme before editing so you can safely test the change.