How Can I Add Image Swatches to the Dawn Theme?

How Can I Add Image Swatches to the Dawn Theme?

vhboy
Tourist
5 0 3

Hi everyone, I hope you're doing well!

I'm using the Dawn theme on Shopify and I would like to add image swatches for product variants (such as colour options), without using any external apps, to keep my store lightweight. I've searched for working code and tutorials, but I haven’t found anything clear or helpful. I’m looking for a working code snippet and precise instructions on where exactly to place it within Shopify’s code editor.

Can anyone help me with this or point me in the right direction? I’d really appreciate it!

Replies 8 (8)

goldi07
Navigator
360 38 69

Hello @vhboy To add image swatches for color variants in the Dawn theme on Shopify (without using apps), you’ll need to customize the main-product.liquid, product-variant-picker.liquid, and global.js files. I’ll walk you through the full process to achieve this cleanly.

 

 

What You'll Achieve:
. Replace the default color variant dropdown or pills with clickable image swatches.

. Keep it lightweight, without apps.

. Show selected state clearly.

 

 

 

Prerequisites:
. You're using Dawn 9.0.0+ (let me know if your version is older).

. Your product variants have images assigned per color.

 

 

 

Step-by-Step Instructions:
1. Assign Images to Each Variant
In your Shopify Admin:

. Go to Products > [Your Product].

. Scroll to Variants, click each one, and upload an image that represents the color.

 

 

2. Edit main-product.liquid
File path: Sections/main-product.liquid

Look for this block (or similar):

{% render 'product-variant-picker', ... %}

It should already be rendering the picker, so no changes needed here unless it’s missing.

 

 

 

3. 

Modify product-variant-picker.liquid
File path: Snippets/product-variant-picker.liquid

Replace the contents only for the color option with image swatches.

Find this loop:

{% for option in product.options_with_values %}

And inside it, look for:

{% if option.name == 'Color' %}

If it doesn’t exist, wrap it yourself like this:

 

Replace or Insert this inside the loop:

{% if option.name == 'Color' %}
  <fieldset class="variant-fieldset">
    <legend class="form__label">{{ option.name }}</legend>
    <div class="swatch-container">
      {% for value in option.values %}
        {% assign downcased_value = value | downcase | replace: ' ', '-' %}
        {% assign variant_image = null %}
        {% for variant in product.variants %}
          {% if variant.options contains value %}
            {% assign variant_image = variant.image %}
            {% break %}
          {% endif %}
        {% endfor %}
        <label class="swatch-label">
          <input type="radio"
                 name="option-{{ option.position }}"
                 value="{{ value }}"
                 form="{{ product_form_id }}"
                 {% if option.selected_value == value %}checked{% endif %}
          >
          {% if variant_image %}
            <img src="{{ variant_image | image_url: width: 50 }}" alt="{{ value }}" class="swatch-image">
          {% else %}
            <span class="swatch-fallback">{{ value }}</span>
          {% endif %}
        </label>
      {% endfor %}
    </div>
  </fieldset>
{% else %}
  {% render 'product-variant-options', product: product, option: option, product_form_id: product_form_id %}
{% endif %}

This creates image swatches instead of default text/pills for Color.

 

 

 

 

4.  Add CSS for Swatches
File path: Assets/base.css or Assets/component-product.css

Scroll to the bottom and add this:

.swatch-container {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 1rem 0;
}

.swatch-label {
  position: relative;
  cursor: pointer;
  border: 2px solid transparent;
  padding: 2px;
  border-radius: 4px;
}

.swatch-label input[type="radio"] {
  display: none;
}

.swatch-label .swatch-image {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid #ccc;
}

.swatch-label input[type="radio"]:checked + .swatch-image {
  border: 2px solid #000;
}

 

Done! Test It Out:
1. Visit a product page with color variants.

2. Swatches should appear.

3. Selecting a swatch should change the product image and update the variant selection.

 

 

 

Would you like me to help add hover titles, color names under images, or support for mobile styling

plz let me know 

 

Thank you 😊

 

 

 

Was I helpful?

Buy me a coffee


APPS BY US :

Professional Customer Accounts APP


Want to modify or custom changes or bug fix on store . Or Need help with your store? Or -Want Complete Storefront
Email me -Goldi184507@gmail.com - Skype: live:.cid.819bad8ddb52736c -Whatsapp: +919317950519
Checkout Some Free Sections Here
vhboy
Tourist
5 0 3

Hello @goldi07 , hope you're doing well. Thank you for helping me out.

I have a few questions, if you don’t mind clearing them up for me:

I couldn’t find where to place the code in product-variant-picker.liquid. I don’t really understand much about loops and things like that — would it be okay if I send you my full code so you can add it for me? Or perhaps you could tell me exactly which line to insert the code on, if that’s not too much trouble.

{% comment %}
  Renders product variant-picker

  Accepts:
  - product: {Object} product object.
  - block: {Object} passing the block information.
  - product_form_id: {String} Id of the product form to which the variant picker is associated.
  Usage:
  {% render 'product-variant-picker', product: product, block: block, product_form_id: product_form_id %}
{% endcomment %}
{%- unless product.has_only_default_variant -%}
  <variant-selects
    id="variant-selects-{{ section.id }}"
    data-section="{{ section.id }}"
    {{ block.shopify_attributes }}
  >
    {%- for option in product.options_with_values -%}
      {%- liquid
        assign swatch_count = option.values | map: 'swatch' | compact | size
        assign picker_type = block.settings.picker_type

        if swatch_count > 0 and block.settings.swatch_shape != 'none'
          if block.settings.picker_type == 'dropdown'
            assign picker_type = 'swatch_dropdown'
          else
            assign picker_type = 'swatch'
          endif
        endif
      -%}
      {%- if picker_type == 'swatch' -%}
        <fieldset class="js product-form__input product-form__input--swatch">
          <legend class="form__label">
            {{ option.name }}:
            <span data-selected-value>
              {{- option.selected_value -}}
            </span>
          </legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- elsif picker_type == 'button' -%}
        <fieldset class="js product-form__input product-form__input--pill">
          <legend class="form__label">{{ option.name }}</legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- else -%}
        <div class="product-form__input product-form__input--dropdown">
          <label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}">
            {{ option.name }}
          </label>
          <div class="select">
            {%- if picker_type == 'swatch_dropdown' -%}
              <span
                data-selected-value
                class="dropdown-swatch"
              >
                {% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
              </span>
            {%- endif -%}
            <select
              id="Option-{{ section.id }}-{{ forloop.index0 }}"
              class="select__select"
              name="options[{{ option.name | escape }}]"
              form="{{ product_form_id }}"
            >
              {% render 'product-variant-options',
                product: product,
                option: option,
                block: block,
                picker_type: picker_type
              %}
            </select>
            <span class="svg-wrapper">
              {{- 'icon-caret.svg' | inline_asset_content -}}
            </span>
          </div>
        </div>
      {%- endif -%}
    {%- endfor -%}

    <script type="application/json" data-selected-variant>
      {{ product.selected_or_first_available_variant | json }}
    </script>
  </variant-selects>
{%- endunless -%}

goldi07
Navigator
360 38 69

Screenshot 2025-05-05 234351.png

 

 

yes, it helps a lot to see your actual product-variant-picker.liquid snippet. I’ll guide you through an exact modification so you can add image swatches for the Color option only, while keeping the other options as they are (dropdowns, buttons, etc.).

 

Where to Insert the Code
We’ll be modifying just one part inside your existing loop:

{%- for option in product.options_with_values -%}

 

That loop is already rendering different picker types (swatch, button, dropdown) based on picker_type.

We’ll now intercept that flow for just the Color option and render our custom image swatch block instead.

 

 

Step-by-Step Code Insert
1. Inside your loop, just after this line:

 

Add this conditional right after the loop starts:

{%- if option.name == 'Color' -%}
  <fieldset class="js product-form__input product-form__input--swatch">
    <legend class="form__label">
      {{ option.name }}:
      <span data-selected-value>
        {{- option.selected_value -}}
      </span>
    </legend>
    <div class="swatch-container">
      {%- for value in option.values -%}
        {%- assign downcased_value = value | downcase | replace: ' ', '-' -%}
        {%- assign variant_image = null -%}
        {%- for variant in product.variants -%}
          {%- if variant.options contains value -%}
            {%- assign variant_image = variant.image -%}
            {%- break -%}
          {%- endif -%}
        {%- endfor -%}
        <label class="swatch-label">
          <input
            type="radio"
            name="options[{{ option.name }}]"
            value="{{ value }}"
            form="{{ product_form_id }}"
            {% if option.selected_value == value %}checked{% endif %}
          >
          {% if variant_image %}
            <img
              src="{{ variant_image | image_url: width: 60 }}"
              alt="{{ value }}"
              class="swatch-image"
            >
          {% else %}
            <span class="swatch-fallback">{{ value }}</span>
          {% endif %}
        </label>
      {%- endfor -%}
    </div>
  </fieldset>
{%- else -%}

 3. Then, scroll down and find this line (which closes your current picker rendering):

{%- endif -%}

4. Just after that, add:

{%- endif -%}

This closes your custom if option.name == 'Color' logic cleanly.

 

 

Your Updated Flow Will Look Like:

 

{%- for option in product.options_with_values -%}

  {%- if option.name == 'Color' -%}
    <!-- Custom image swatch fieldset here -->
  {%- else -%}
    <!-- Your existing picker logic for other options -->
    {%- if picker_type == 'swatch' -%}
      ...
    {%- elsif picker_type == 'button' -%}
      ...
    {%- else -%}
      ...
    {%- endif -%}
  {%- endif -%}

{%- endfor -%}

 

 

CSS Reminder
In your Assets/base.css (or component-product.css), don’t forget to add:

.swatch-container {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}

.swatch-label {
  position: relative;
  cursor: pointer;
  border: 2px solid transparent;
  padding: 2px;
  border-radius: 4px;
}

.swatch-label input[type="radio"] {
  display: none;
}

.swatch-image {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid #ccc;
}

.swatch-label input[type="radio"]:checked + .swatch-image {
  border: 2px solid #000;
}

 

 

 

try this and let me if this work i also add a sacreenshot 

thank you 😊

Was I helpful?

Buy me a coffee


APPS BY US :

Professional Customer Accounts APP


Want to modify or custom changes or bug fix on store . Or Need help with your store? Or -Want Complete Storefront
Email me -Goldi184507@gmail.com - Skype: live:.cid.819bad8ddb52736c -Whatsapp: +919317950519
Checkout Some Free Sections Here
vhboy
Tourist
5 0 3

I managed to add it successfully, but when I click on the variant, which is now an image, it doesn't switch. It's only possible to view the colour without being able to change it. Do you know how to fix this? I’ll send you my code so you can see how it turned out.

 

{% comment %}
  Renders product variant-picker

  Accepts:
  - product: {Object} product object.
  - block: {Object} passing the block information.
  - product_form_id: {String} Id of the product form to which the variant picker is associated.
  Usage:
  {% render 'product-variant-picker', product: product, block: block, product_form_id: product_form_id %}
{% endcomment %}
{%- unless product.has_only_default_variant -%}
  <variant-selects
    id="variant-selects-{{ section.id }}"
    data-section="{{ section.id }}"
    {{ block.shopify_attributes }}
  >
    {%- for option in product.options_with_values -%}
{%- if option.name == 'Color' -%}
  <fieldset class="js product-form__input product-form__input--swatch">
    <legend class="form__label">
      {{ option.name }}:
      <span data-selected-value>
        {{- option.selected_value -}}
      </span>
    </legend>
    <div class="swatch-container">
      {%- for value in option.values -%}
        {%- assign downcased_value = value | downcase | replace: ' ', '-' -%}
        {%- assign variant_image = null -%}
        {%- for variant in product.variants -%}
          {%- if variant.options contains value -%}
            {%- assign variant_image = variant.image -%}
            {%- break -%}
          {%- endif -%}
        {%- endfor -%}
        <label class="swatch-label">
          <input
            type="radio"
            name="options[{{ option.name }}]"
            value="{{ value }}"
            form="{{ product_form_id }}"
            {% if option.selected_value == value %}checked{% endif %}
          >
          {% if variant_image %}
            <img
              src="{{ variant_image | image_url: width: 60 }}"
              alt="{{ value }}"
              class="swatch-image"
            >
          {% else %}
            <span class="swatch-fallback">{{ value }}</span>
          {% endif %}
        </label>
      {%- endfor -%}
    </div>
  </fieldset>
{%- else -%}      
      {%- liquid
        assign swatch_count = option.values | map: 'swatch' | compact | size
        assign picker_type = block.settings.picker_type

        if swatch_count > 0 and block.settings.swatch_shape != 'none'
          if block.settings.picker_type == 'dropdown'
            assign picker_type = 'swatch_dropdown'
          else
            assign picker_type = 'swatch'
          endif
        endif
      -%}
      {%- if picker_type == 'swatch' -%}
        <fieldset class="js product-form__input product-form__input--swatch">
          <legend class="form__label">
            {{ option.name }}:
            <span data-selected-value>
              {{- option.selected_value -}}
            </span>
          </legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- elsif picker_type == 'button' -%}
        <fieldset class="js product-form__input product-form__input--pill">
          <legend class="form__label">{{ option.name }}</legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- else -%}
        <div class="product-form__input product-form__input--dropdown">
          <label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}">
            {{ option.name }}
          </label>
          <div class="select">
            {%- if picker_type == 'swatch_dropdown' -%}
              <span
                data-selected-value
                class="dropdown-swatch"
              >
                {% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
              </span>
            {%- endif -%}
            <select
              id="Option-{{ section.id }}-{{ forloop.index0 }}"
              class="select__select"
              name="options[{{ option.name | escape }}]"
              form="{{ product_form_id }}"
            >
              {% render 'product-variant-options',
                product: product,
                option: option,
                block: block,
                picker_type: picker_type
              %}
            </select>
            <span class="svg-wrapper">
              {{- 'icon-caret.svg' | inline_asset_content -}}
            </span>
          </div>
        </div>
      {%- endif -%}
  {%- endif -%}
      
    {%- endfor -%}

    <script type="application/json" data-selected-variant>
      {{ product.selected_or_first_available_variant | json }}
    </script>
  </variant-selects>
{%- endunless -%}

image.png

goldi07
Navigator
360 38 69

hey @vhboy Thanks for sharing your code, — you're super close! The issue is that while your custom image swatches are rendering perfectly, they’re missing the JavaScript trigger that tells Shopify’s <variant-selects> component to update the selected variant when you change the radio button.

 

 

 

Here’s what’s happening:

. The <input type="radio"> is being rendered correctly.

. But Shopify’s built-in variant-selects.js component listens for change events on the <select> and default <input> elements it renders.

. Since we’ve custom-rendered the radio inputs ourselves, we need to dispatch a change event manually for them to work with Dawn’s logic.

 

 

 

Solution: Add JS Event Trigger for Your Custom Radio Inputs
Step 1: Add a small JS block below your </variant-selects> (right after the </variant-selects> tag)
Add this just after:

</variant-selects>
<script>
  document.querySelectorAll('[name^="options[Color]"]').forEach((input) => {
    input.addEventListener('change', function () {
      // Trigger native change event for Shopify's variant-selects
      const variantSelectsEl = this.closest('variant-selects');
      if (variantSelectsEl) {
        variantSelectsEl.dispatchEvent(new Event('change', { bubbles: true }));
      }
    });
  });
</script>

 

 

 

Bonus: Fix Image Click Target (Optional UX)
Make your <img> inside the label clickable by adding a for and id link:

Update your input and image area like this:

<input
  type="radio"
  id="swatch-{{ option.name | handle }}-{{ value | handle }}"
  name="options[{{ option.name }}]"
  value="{{ value }}"
  form="{{ product_form_id }}"
  {% if option.selected_value == value %}checked{% endif %}
/>
<label class="swatch-label" for="swatch-{{ option.name | handle }}-{{ value | handle }}">
  {% if variant_image %}
    <img
      src="{{ variant_image | image_url: width: 60 }}"
      alt="{{ value }}"
      class="swatch-image"
    >
  {% else %}
    <span class="swatch-fallback">{{ value }}</span>
  {% endif %}
</label>

So, instead of nesting the <input> inside the <label>, you explicitly pair them via id and for. Shopify's default styles and behavior work best with this structure.

 

 

Once you add the JavaScript snippet and update the label structure if you want, the swatches will trigger the correct variant selection, and the product media / price / availability will update accordingly — just like with native controls.

 

 

if you need more help plz let me know 

Thank you  😊

 

 

 

Was I helpful?

Buy me a coffee


APPS BY US :

Professional Customer Accounts APP


Want to modify or custom changes or bug fix on store . Or Need help with your store? Or -Want Complete Storefront
Email me -Goldi184507@gmail.com - Skype: live:.cid.819bad8ddb52736c -Whatsapp: +919317950519
Checkout Some Free Sections Here
vhboy
Tourist
5 0 3

Hi my friend, how are you? I hope you're having an excellent day. First of all, I want to thank you for your patience and help—it's really been a huge support!

I managed to do everything you mentioned, but when I click on the image, it still doesn't change the variant. I'd like to know what's missing or what I might have done wrong. I'll leave my code below.

{% comment %}
  Renders product variant-picker

  Accepts:
  - product: {Object} product object.
  - block: {Object} passing the block information.
  - product_form_id: {String} Id of the product form to which the variant picker is associated.
  Usage:
  {% render 'product-variant-picker', product: product, block: block, product_form_id: product_form_id %}
{% endcomment %}
{%- unless product.has_only_default_variant -%}
  <variant-selects
    id="variant-selects-{{ section.id }}"
    data-section="{{ section.id }}"
    {{ block.shopify_attributes }}
  >
    {%- for option in product.options_with_values -%}
      {%- if option.name == 'Color' -%}
  <fieldset class="js product-form__input product-form__input--swatch">
    <legend class="form__label">
      {{ option.name }}:
      <span data-selected-value>
        {{- option.selected_value -}}
      </span>
    </legend>
    <div class="swatch-container">
      {%- for value in option.values -%}
        {%- assign downcased_value = value | downcase | replace: ' ', '-' -%}
        {%- assign variant_image = null -%}
        {%- for variant in product.variants -%}
          {%- if variant.options contains value -%}
            {%- assign variant_image = variant.image -%}
            {%- break -%}
          {%- endif -%}
        {%- endfor -%}
        <label class="swatch-label">
          <input
            type="radio"
            id="swatch-{{ option.name | handle }}-{{ value | handle }}"
            name="options[{{ option.name }}]"
            value="{{ value }}"
            form="{{ product_form_id }}"
            {% if option.selected_value == value %}checked{% endif %}}
          />
          <label class="swatch-label" for="swatch-{{ option.name | handle }}-{{ value | handle }}">
            {% if variant_image %}
              <img
                src="{{ variant_image | image_url: width: 60 }}"
                alt="{{ value }}"
                class="swatch-image"
              >
          {% else %}
            <span class="swatch-fallback">{{ value }}</span>
          {% endif %}
        </label>
      {%- endfor -%}
    </div>
  </fieldset>
{%- else -%}
      {%- liquid
        assign swatch_count = option.values | map: 'swatch' | compact | size
        assign picker_type = block.settings.picker_type

        if swatch_count > 0 and block.settings.swatch_shape != 'none'
          if block.settings.picker_type == 'dropdown'
            assign picker_type = 'swatch_dropdown'
          else
            assign picker_type = 'swatch'
          endif
        endif
      -%}
      {%- if picker_type == 'swatch' -%}
        <fieldset class="js product-form__input product-form__input--swatch">
          <legend class="form__label">
            {{ option.name }}:
            <span data-selected-value>
              {{- option.selected_value -}}
            </span>
          </legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- elsif picker_type == 'button' -%}
        <fieldset class="js product-form__input product-form__input--pill">
          <legend class="form__label">{{ option.name }}</legend>
          {% render 'product-variant-options',
            product: product,
            option: option,
            block: block,
            picker_type: picker_type
          %}
        </fieldset>
      {%- else -%}
        <div class="product-form__input product-form__input--dropdown">
          <label class="form__label" for="Option-{{ section.id }}-{{ forloop.index0 }}">
            {{ option.name }}
          </label>
          <div class="select">
            {%- if picker_type == 'swatch_dropdown' -%}
              <span
                data-selected-value
                class="dropdown-swatch"
              >
                {% render 'swatch', swatch: option.selected_value.swatch, shape: block.settings.swatch_shape %}
              </span>
            {%- endif -%}
            <select
              id="Option-{{ section.id }}-{{ forloop.index0 }}"
              class="select__select"
              name="options[{{ option.name | escape }}]"
              form="{{ product_form_id }}"
            >
              {% render 'product-variant-options',
                product: product,
                option: option,
                block: block,
                picker_type: picker_type
              %}
            </select>
            <span class="svg-wrapper">
              {{- 'icon-caret.svg' | inline_asset_content -}}
            </span>
          </div>
        </div>
      {%- endif -%}
        {%- endif -%}
    {%- endfor -%}

    <script type="application/json" data-selected-variant>
      {{ product.selected_or_first_available_variant | json }}
    </script>
  </variant-selects>
  <script>
  document.querySelectorAll('[name^="options[Color]"]').forEach((input) => {
    input.addEventListener('change', function () {
      // Trigger native change event for Shopify's variant-selects
      const variantSelectsEl = this.closest('variant-selects');
      if (variantSelectsEl) {
        variantSelectsEl.dispatchEvent(new Event('change', { bubbles: true }));
      }
    });
  });
</script>
{%- endunless -%}
vhboy
Tourist
5 0 3

I can't do what this video suggests with existing products — I'd have to register everything from scratch, and that wasn't what I was looking for. But I really appreciate your help, mate. @gr_trading 

gr_trading
Shopify Partner
2049 149 208

Hi @vhboy ,

 

Please refer the below video to see how you can activate color swatch in DAWN theme.

 

For any custom development WhatsApp or connect at Email ID: support@grtrading.in for quick consultation. | Shopify Free codes
To support Buy Me a Coffee