DEV Community

Eli
Eli

Posted on • Originally published at clawmama.run

Your variant has one image, your product has twenty — the Shopify gallery filter question, finally answered

I found this one in a Shopify Community thread that collected 13 posts in 24 hours and still has no accepted answer. A merchant on the Horizon theme asked the question store owners have asked for a decade: when a shopper picks Black, show only the black images — no paid app, no Plus.

The thread is a museum of every classic wrong answer: a layout setting sold as filtering, the one-image-per-variant confusion, a "send me your store URL" service pitch, and a working code snippet the OP couldn't get running. I've watched this question get half-answered for years, so here's the whole picture — including why the "correct" code so often "doesn't work."

Root cause: two things that look alike

Shopify's data model gives you two different things:

  1. A variant can carry its own mediavariant.featured_image / variant.featured_media in Liquid, ProductVariant.media in the Admin GraphQL API. This is what you set when you assign an image to a variant in the admin. Exactly one is "featured," and themes (Horizon included) swap it into the main gallery slot on selection. Native, free, every plan.
  2. The product's media setproduct.media — is a flat pool shared by all variants. Any image not assigned to a variant belongs to every color simultaneously. There is no native structure for "these five images belong to Black."

So "show only the selected variant's images" is two different problems:

Case What you want Native support
A One representative image per color, swapped on selection Yes — variant featured image, every theme
B A gallery per color (3–8 shots each), filtered on selection No — not in the data model, not a Horizon setting

If you're in Case A, stop here: assign each variant's featured image in Products → your product → Variants. On Horizon, additionally enable "Hide other variants' media after selecting a variant" in the Media Gallery block settings (field report, community-verified by three independent posters in topic 659928 — it hides down to the one assigned image per variant, not a multi-image set).

Everything below is Case B.

Minimal reproduction

Five minutes on a dev store:

  1. Create a product "Test Tee", option Color: Black, White.
  2. Upload 6 images: 3 black, 3 white. Assign the first black image as Black's featured image, first white as White's.
  3. Publish with Horizon, Media Gallery in grid mode (the default).
  4. Select Black on the storefront: the main image swaps — and the other 5 images, including all 3 white shots, stay in the gallery. Select White: same in reverse.

That's the ceiling of native behavior. No toggle changes it.

Route 1 — Alt-text tagging + gallery filter (free, code, no app)

Tag every color image with its color in Alt text (Products → product → click image → Alt text → Black, White, …; leave shared images like size charts untagged). Then filter the gallery on the selected option value, in a Custom Liquid block or section on the product template:

{% assign current_variant = product.selected_or_first_available_variant %}
{% assign selected_color = current_variant.options.first %}

{% for media in product.media %}
  {% if media.media_type == 'image' %}
    {% if media.alt == blank or media.alt == selected_color %}
      <img
        src="{{ media | image_url: width: 800 }}"
        alt="{{ media.alt | escape }}"
        loading="lazy">
    {% endif %}
  {% endif %}
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

The notes that make or break this in production:

  • product.media filtered with where: 'media_type', 'image' is the documented pattern; media.alt is the string you typed in the admin. Both documented.
  • Alt text must match the option value exactlyBlackblackBlack (trailing space). This, plus browser cache, is why the OP reported the community code "not working."
  • The snippet filters on first render only. Switching colors is a client-side event — you need a small JS listener on the variant picker that re-filters (or hides/shows) gallery items without a reload. This is the half people paste-and-pray without.
  • Untagged images stay visible for every color — exactly what you want for size charts and lifestyle shots.
  • This approach was tested on a live Horizon store (grid layout) with before/after screenshots in the thread; the untagged size chart correctly persisted across colors.

Route 2 — Shopify Combined Listings (free, first-party, Plus only)

If each color deserves its own full media set — and its own URL for ads and SEO — the first-party answer is Combined Listings: each color is a separate product with its own images, and the combined listing displays all child products on one product page. Zero code. The catch is hard: Combined Listings are available only to stores on a Shopify Plus plan (shopify.dev, verified 2026-08-05). For the OP, who excluded both apps and Plus, this route is out — but it's the correct answer for Plus stores, and worth knowing before you write any Liquid.

Route 3 — Apps

Exist, work, out of scope: the constraint was "no paid app," and Routes 1–2 cover the free space completely.

Decision tree

One image per color?            → Variant featured image (+ Horizon's
                                  "hide other variants' media" toggle). Done.
Multiple images per color?
  ├─ On Plus?                   → Combined Listings. No code.
  └─ Not on Plus?               → Alt-text tagging + Custom Liquid/JS filter.
                                   Match alt text to option names exactly.
Enter fullscreen mode Exit fullscreen mode

Checklist

  • Decide your case first: one image per color (native) vs. a gallery per color (code or Plus).
  • Case A: assign each variant's featured image; on Horizon, enable "Hide other variants' media" in the Media Gallery block.
  • Case B, Route 1: tag every color image's Alt text with the exact option value (case, spelling, trailing spaces).
  • Filter product.media by media_type == 'image' and media.alt == selected_color; leave media.alt == blank items visible for shared shots.
  • Add the JS listener on variant change — server-rendered Liquid only filters the first paint.
  • Test on grid and carousel; the layout setting changes markup, not filtering.
  • Hard-refresh / incognito before concluding "the code doesn't work" — stale cached HTML is the #1 false negative.
  • On Plus? Skip all of it: Combined Listings gives each color its own product and media set, natively.

Community thread content (the Horizon "Hide other variants' media" setting, the tested alt-text solution, the OP's failed implementation) is a field report from Shopify Community topic 659928; every load-bearing technical claim is verified against Shopify's official documentation (Liquid variant.featured_image/featured_media, media.alt, product.media where-filter, Admin GraphQL ProductVariant.media, Combined Listings) as of 2026-08-05.

Originally published at https://clawmama.run/blog/shopify-horizon-variant-media-filter/

Top comments (0)