Your Variant Has One Image, Your Product Has Twenty: Filtering the Gallery by Selected Variant
A merchant on the Horizon theme asked the question Shopify store owners have asked for a decade: when a shopper picks Black, show only the black images — no paid app, no Plus. Within 24 hours the thread collected every classic failure mode: a layout setting sold as filtering, the one-image-per-variant confusion, a service pitch, and a working-but-fragile code snippet the OP could not get running. This is the answer that thread never got.
TL;DR: “Show only the selected variant’s images” is two different problems wearing one name. Case A — one representative image per color — is native and free on every plan: assign each variant its featured image in the product admin, and on Horizon additionally enable the “Hide other variants’ media after selecting a variant” setting in the Media Gallery block (field report, community-verified in topic 659928; it hides down to the one assigned image per variant). Case B — a whole gallery per color — has no native support at all: product.media is a flat pool shared by every variant, and no Horizon setting changes that. The free, non-Plus, no-app route for Case B is alt-text tagging plus a Custom Liquid/JS filter on product.media; on Plus, Shopify Combined Listings is the zero-code answer. Grid vs. carousel is a layout choice, never a filter.
This is part 1 of our Shopify theme production traps series. The demand evidence is a live Shopify Community thread (topic 659928, a field report we label as such throughout): 13 posts in 24 hours, two follow-ups from the OP, and no accepted answer.
The problem
On August 2, 2026, a merchant posted the question (community topic 659928, Shopify Discussion):
Horizon theme — show only images of the selected color variant. 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.
Within 24 hours the thread collected 13 posts and every classic failure mode of this question:
- The settings red herring. The first reply pointed at the Media Gallery block’s layout setting — grid vs. carousel. Layout is not filtering; both layouts show every image.
- The one-image confusion. Two answerers described assigning a variant image in the admin and concluded “it just works.” It does — for exactly one image per color. The merchant’s real question is about a set of images per color.
- The service pitch. “Send me your store URL and I’ll check your theme” — not an answer.
- The working-but-fragile code. One developer posted a tested alt-text + Custom Liquid solution with before/after screenshots. The OP tried it and reported back: “I couldn’t get it working.”
The thread ends with no accepted answer and an OP who still doesn’t have a filtered gallery. A second poster linked a parallel Horizon thread (v4.1.3, different store, same symptom) — this is a crowd-level confusion, not one merchant’s bad day.
Root cause
The confusion exists because Shopify’s data model gives you two different things that look alike:
- A variant can carry its own media — exposed in Liquid as
variant.featured_image/variant.featured_media, and in the Admin GraphQL API asProductVariant.media. This is what you set when you click a variant in the product admin and assign an image. Exactly one of these is “featured” for the variant, and themes (Horizon included) swap it into the main gallery slot when a shopper selects that variant. This is native, free, and works on every plan. - The product’s media set —
product.mediain Liquid — is a flat pool shared by all variants. Any image not assigned to a variant belongs to every color simultaneously. There is no native data structure for “this group of five images belongs to Black.”
So “show only the selected variant’s images” decomposes into two completely 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 |
Everything that follows is about Case B. If you are in Case A, stop reading: assign each variant its featured image in Products → your product → Variants, and on Horizon additionally enable the “Hide other variants’ media after selecting a variant” setting in the Media Gallery block (theme editor → product template → Media Gallery block settings). Field report, community-verified by three independent posters in topic 659928 (#5, #10 with screenshot, #11); it hides down to the one assigned image per variant, not a multi-image set.
Minimal reproduction
You can reproduce the exact problem in five minutes on a development store:
- Create a product “Test Tee” with option Color: values
Black,White. - Upload 6 images: 3 black shots, 3 white shots. Assign the first black image as the Black variant’s featured image, first white image as White’s.
- Publish with Horizon, product template, Media Gallery in grid mode (Horizon’s default).
- On the storefront, select Black: the main image swaps to the black shot — and the other 5 images, including all 3 white shots, remain in the gallery. Select White: same story in reverse.
That is the ceiling of native behavior. No toggle changes it.
Solution
Three routes, in order of preference for a non-Plus store that refuses apps.
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 → type Black, White, …; leave shared images like size charts untagged). Then filter the gallery on the selected option value. The Liquid half, 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 %}Notes that make or break this in production:
product.mediafiltered withwhere: 'media_type', 'image'is the documented pattern;media.altis the alt text string you typed in the admin. Both are documented Liquid properties.- The alt text must match the option value exactly —
Black≠black≠Black(trailing space). This, plus browser cache, is why the OP in 659928 reported the community code “not working.” - The snippet above filters on first render. Selecting a different color on the storefront 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 page reload. This is the half people paste-and-pray without.
- Untagged images stay visible for every color, which is 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 genuinely deserves its own full media set — and its own URL for ads and SEO — the first-party answer is Shopify 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 explicitly excluded both apps and Plus, this route is out — but it is the correct answer for Plus stores and worth knowing before you write any Liquid.
Route 3 — Apps
Exist, work, out of scope: the OP’s 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.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 in the product admin; 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.mediabymedia_type == 'image'andmedia.alt == selected_color; leavemedia.alt == blankitems visible for shared shots. - Add the JS listener on variant change — server-rendered Liquid alone 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.
- Grid vs. carousel is a layout choice, never a filter — don’t let anyone sell you a settings tour as a solution.
Where an agent fits
Picking a route is a one-time decision. The ongoing work is everything around it: keeping alt-text tags in sync as new product photos are uploaded, spotting variants whose featured image was never assigned, re-testing the gallery after every theme update, and catching the “it stopped working after we changed the option name” class of regressions before a customer does. That is a scheduled store-hygiene loop — exactly the kind of thing a store operations agent can own: check, compare against the rules, flag the exception to a human with the receipts. We build ready-to-use agents for loops like this; if that’s the hole in your week, they’re worth a look.
Verified against Shopify’s official documentation (Liquid variant.featured_image/featured_media, media.alt and the product.media where-filter pattern, Admin GraphQL ProductVariant.media, Combined Listings availability and behavior) as of August 2026. Community thread content (the Horizon “Hide other variants’ media” setting, the tested alt-text solution, the OP’s failed implementation, the parallel Horizon thread) is a field report from Shopify Community topic 659928 and is labeled as such wherever it appears above.