Why does clicking a color swatch change the selection but not the product photo?
The swatch itself looks like it's working. It highlights, it shows a checkmark. But the big product photo just sits there showing the wrong color. That's almost always a wiring problem, not a swatch problem.
What's going on
A merchant customizes their product page swatches, whether through a theme setting, a custom CSS or JavaScript tweak, or a swatch app, and everything looks right at first glance: clicking Navy highlights the navy swatch, clicking Red highlights red. But the main product image, and often the gallery thumbnails, never changes to match. The customer is left staring at a red shirt while the page insists they've selected navy.
This is a classic symptom of a disconnect between the visual swatch element and the actual form control the theme's JavaScript is listening to. In Online Store 2.0 themes, the variant picker is generally a real HTML input or select element wrapped in a custom component. That component listens for a change on the underlying input, looks up the matching variant, and, if that variant has an assigned image, updates the media gallery to match.
When a swatch is added purely as a decorative overlay, a colored circle placed on top of or instead of the real radio button or label, clicking it can update its own appearance without ever triggering the underlying input's change behavior. The variant-picker logic never hears about the click, so it never runs its media-update step, and the product photo stays frozen on whatever variant loaded first. The same break happens when a merchant assigns swatch colors through a metafield or app but never links the corresponding variant to an actual image in Shopify admin. In that case the selection change fires correctly, but there is simply no image to switch to.
Because this depends on exactly how the swatches were implemented, whether native theme swatches, a customized swatch block, a third-party app, or hand-written code, the right fix depends on which of those is true on the storefront in question.
Why it happens
The root cause is almost always a broken handoff between two separate systems: the clickable swatch UI, which only needs to change its own appearance, and the variant-selection logic, which needs to know a real selection changed so it can find the matching variant and swap media. Native swatches in most modern themes keep these tied together because the swatch is styled directly on top of a real input, so clicking it is clicking the input and the underlying change behavior always fires.
That link breaks in a few common ways. A swatch may have been built or customized so the clickable element is a separate div or button layered over the input rather than the input itself, requiring code to manually re-trigger the change behavior, and that step gets missed. A product's variants may have swatch colors assigned through a metafield or app but never have an actual photo assigned to the individual variants in admin, so there is nothing for the gallery to switch to even when everything fires correctly. Or a swatch app or custom code intercepts the click and runs its own function instead of triggering the theme's native variant-change flow, so the theme's built-in media-swap logic never runs at all.
5 ways to fix it
Confirm every variant actually has an assigned image
In Shopify admin, open the product and check each variant under the Variants section. Each one needs its own photo assigned, not just photos sitting in the general product gallery. If a variant has no image tied to it, no amount of JavaScript will make the gallery switch, because there is genuinely nothing to switch to. This takes a few minutes and rules out the simplest cause first.
Check whether you're using native theme swatches or a customized version
Built-in color swatch options in Online Store 2.0 themes typically style the actual radio input or select element, so a click always registers as a real selection. If someone added a custom swatch look through extra CSS or JavaScript, or through the theme's code editor, try reverting to the native swatch style temporarily. If the photo updates correctly with native swatches, the custom styling is the culprit.
Have a developer re-wire the swatch click to trigger a real selection
If a custom swatch needs to stay for a look the native option can't achieve, the fix is to make sure clicking it either checks the underlying input directly, so the browser fires its own change event, or manually dispatches that event on the input after updating its value. This keeps the theme's existing variant logic doing what it already does. There is no need to rebuild the image-swap behavior from scratch, just make sure it gets notified.
If a swatch app is involved, check its variant-image sync settings
Many swatch apps have their own settings controlling whether they trigger the theme's native selection events or just restyle the display. Look for an option along the lines of sync with variant selector or trigger native events, and make sure it's enabled. If the app's support team confirms it doesn't support your theme's specific markup, that's a compatibility gap worth flagging to them directly.
Test in an unpublished duplicate theme before rolling out a fix
Because this bug involves front-end JavaScript timing, duplicate the live theme, make the swatch-wiring fix there, and click through every variant combination, including options with no image assigned, before publishing. A fix that works for the first three color swatches but silently breaks on a fourth is easy to miss without deliberately testing every option.
Bottom line
This is a wiring bug, not a swatch design flaw, and in most cases it is fixable for free by checking variant image assignments and confirming the swatch click still triggers a real selection. If you're running a heavily customized or app-based swatch setup and don't want to touch theme code yourself, a developer or a swatch app confirmed compatible with your theme can usually resolve it in under an hour.
Browse the rest of the problem library, run a free storefront scan to catch issues like this automatically, or email us and we'll work out a custom solution for it.