Skip to content
Product Image Badge Overlays - Free Tutorial
Browse other ways to boost conversion rate & profit

Product Image Badge Overlays - Free Tutorial

In this tutorial, we show you how to easily add product image overlay badges across your entire Shopify catalog, without needing to edit any of your product images.

These badges can help boost your conversion rate by highlighting your products' features and stores' promotions. Easily swap them in and out for new sales and promotions.

It's easy to add this feature to your store - no apps or coding knowledge needed.

Compatible Themes: This code should work on all free Shopify themes (Dawn, Refresh, Craft, Studio, Publisher, Crave, Origin, Taste, Colorblock, Sense, Ride, Spotlight).

 

Create metafields

  • Name: Show Product Media Badge
    • Key: show_product_media_badge
    • Type: True or false
  • Product Media Dynamic Badge Image
    • Key: product_media_dynamic_badge_image
    • Type: File (One file)

Edit settings_schema.json

  {
    "name": "Custom Product Media Badge",
    "settings": [
      {
        "type": "checkbox",
        "id": "show_product_media_badges",
        "label": "Show Custom Badges on Product Page Media",
        "default": true
      },
      {
        "type": "select",
        "id": "product_media_badge_behavior",
        "label": "Badge Behavior",
        "options": [
          {
            "value": "featured",
            "label": "Show on featured image"
          },
          {
            "value": "initial",
            "label": "Show only on initial variant"
          }
        ],
        "default": "featured"
      },
      {
        "type": "select",
        "id": "product_media_badge_position",
        "options": [
          {
            "value": "bottom left",
            "label": "t:settings_schema.badges.settings.position.options__1.label"
          },
          {
            "value": "bottom right",
            "label": "t:settings_schema.badges.settings.position.options__2.label"
          },
          {
            "value": "top left",
            "label": "t:settings_schema.badges.settings.position.options__3.label"
          },
          {
            "value": "top right",
            "label": "t:settings_schema.badges.settings.position.options__4.label"
          }
        ],
        "default": "bottom left",
        "label": "Badge Position"
      },
      {
        "type": "select",
        "id": "product_media_badge_type",
        "options": [
          {
            "value": "fixed_image",
            "label": "Fixed Image Badge"
          },
          {
            "value": "dynamic_image",
            "label": "Dynamic Image Badge"
          }
        ],
        "default": "fixed_image",
        "label": "Badge Type"
      },
      {
        "type": "image_picker",
        "id": "product_media_badge_image",
        "label": "Badge Image"
      },
      {
        "type": "range",
        "id": "product_media_badge_width_ratio",
        "min": 10,
        "max": 30,
        "step": 1,
        "default": 20,
        "label": "Badge Width Ratio (%)"
      }
    ]
  },

Edit main-product.liquid

  {{ 'component-product-media-badges.css' | asset_url | stylesheet_tag }}
  {%- style -%}
    .product__badge {
      width: {{ settings.product_media_badge_width_ratio }}%;
    }
  
    @media screen and (max-width: 749px) {
      .product__badge {
        width: {{ settings.product_media_badge_width_ratio }}%;
      }
    }
    {%- endstyle -%}

Edit product-media-gallery.liquid

Add attribute to media-gallery element

data-badge-behavior="{{ settings.product_media_badge_behavior }}"

Add product media badge container for featured media and remaining media

          <div class="product__media-badge-container">
            {% if settings.show_product_media_badges and product.metafields.custom.show_product_media_badge %}
              <div class="product__badge-container">
                <div class="product__badge {{ settings.product_media_badge_position }}">
                  <div class="product__badge-wrapper">
                    {% if settings.product_media_badge_type == "fixed_image" and settings.product_media_badge_image != blank %}
                      {{ settings.product_media_badge_image | image_url: width: 400 | image_tag: class: 'product-badge-image' }}
                    {% elsif settings.product_media_badge_type == "dynamic_image" and product.metafields.custom.product_media_dynamic_badge_image != blank %}
                      {{ product.metafields.custom.product_media_dynamic_badge_image | image_url: width: 400 | image_tag: class: 'product-badge-image' }}
                    {%- endif -%}
                  </div>
                </div>
              </div>
            {% endif %}
            
            ...existing code for image render...
            
          </div>

Add badge--hidden class to remaining media

<div class="product__badge-container badge--hidden">

Create new asset component-product-media-badges.css

.product__media-badge-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.product__badge-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 2;
}

.product__badge-container.badge--hidden {
  display: none;
}

.product__media-item:not(.is-active) .product__badge-container {
  display: none;
}

.product__badge {
  position: absolute;
  padding: 1.5rem;
  display: flex;
}

.product__badge-wrapper {
  width: 100%;
  padding-top: 100%; 
  position: relative;
}

.product__badge img,
.product-badge-image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

.product__badge.top { top: 0; }
.product__badge.bottom { bottom: 0; }
.product__badge.left { left: 0; }
.product__badge.right { right: 0; }

.product__badge.top.left { top: 10px; left: 10px; }
.product__badge.top.right { top: 10px; right: 10px; }
.product__badge.bottom.left { bottom: 10px; left: 10px; }
.product__badge.bottom.right { bottom: 10px; right: 10px; }

.product__media-badge-container .product-media-container {
  width: 100%;
  height: 100%;
}

@media screen and (max-width: 749px) {
  .product__badge {
    padding: 1rem;
  }

  .slider--mobile .product__media-item:not(.is-active) .product__badge-container {
    display: none;
  }

  .product__badge.top.left { top: 1px; left: 1px; }
  .product__badge.top.right { top: 1px; right: 1px; }
  .product__badge.bottom.left { bottom: 1px; left: 1px; }
  .product__badge.bottom.right { bottom: 1px; right: 1px; }
}

Edit product-info.js

Edit constructor method

        this.badgeBehavior = this.querySelector('media-gallery').dataset.badgeBehavior;
        const initialVariant = this.getSelectedVariant(this);
        this.initialVariantMediaId = initialVariant?.featured_media?.id?.toString();

Add new method

      updateBadgeVisibility(variantFeaturedMediaId) {
        const mediaItems = this.querySelectorAll('.product__media-item');
        mediaItems.forEach(item => {
          const badgeContainer = item.querySelector('.product__badge-container');
          if (badgeContainer) {
            const isActiveMedia = item.dataset.mediaId === `${this.dataset.section}-${variantFeaturedMediaId}`;
      
            if (this.badgeBehavior === 'featured') {
              badgeContainer.classList.toggle('badge--hidden', !isActiveMedia);
            } else if (this.badgeBehavior === 'initial') {
              const mediaItemId = item.dataset.mediaId.split('-').pop();
              const isInitialVariantMedia = mediaItemId === this.initialVariantMediaId;
              badgeContainer.classList.toggle('badge--hidden', !(isActiveMedia && isInitialVariantMedia));
            }
          }
        });
      }

Call updateBadgeVisibility in updateMedia method

this.updateBadgeVisibility(variantFeaturedMediaId);

Browse other ways to boost conversion rate & profit