Show Sale Badge if one of the product variants is on sale

Topic summary

A merchant wants to display a “SALE” badge on product cards when any variant is on sale, but their current code only shows the badge when all variants are discounted.

The Problem:

  • Current logic compares product.compare_at_price vs product.price (both represent the lowest values across all variants)
  • This fails when only some variants have sale pricing
  • Example: A thermal imager has bundled variants on sale but one standalone variant at full price

Technical Issues Identified:

  • The product in question may not have compare_at_price set at all
  • Shopify’s product-level price properties can come from different variants, making savings calculations unreliable
  • False positives can occur if compare_at_price is set lower than the actual price

Recommended Solution:
Loop through all variants individually to check if any have variant.compare_at_price > variant.price. Set a flag (has_sale_variant) when found, then display the badge based on that flag. A complete code snippet using this approach was provided, which breaks the loop once a sale variant is detected for efficiency.

Summarized with AI on October 29. AI used: claude-sonnet-4-5-20250929.

I’ve configured a “ON SALE” badge to show when a product is on sale.

However this seems to only work when ALL variants are on sale. I have a product where one of the variants is not on sale but the rest of the variants are. I’d still like the SALE badge to show on the product card (not product page).

This is the code snippet I’m currently using (yes it’s messy as I was playing around with showing the $ off)

One of the variants is $7750 CAD and not on sale; but the bundled items are on sale and because of that I’d like to have the “SALE” badge show on the card level.

Product Card:


            {%- if card_product.available == false -%}
              
                {{- 'products.product.sold_out' | t -}}
              
            {%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
               
                {% comment %}
                {{- 'products.product.on_sale' | t -}} 
              {% endcomment %}
      {%- assign savings = card_product.compare_at_price | minus: card_product.price | times: 0.01  -%}

ON SALE 
              {% comment %} ${{ savings }} {% endcomment %}
              
            {%- endif -%}
          

1 Like

I’m getting a syntax error with that snippet

It looks like a standard Dawn code which was not changed much since your version (https://github.com/Shopify/dawn/blob/main/snippets/card-product.liquid#L127-L145) with your savings code shown instead of the sale badge.

Looked at your site and this product does not seem to have compare_at_price set at all.

To better understand the logic of card_product.compare_at_price > card_product.price :

product.price is the minimal price of all variants

product.compare_at_price is the minimal compare_at_price of all variants

https://shopify.dev/docs/api/liquid/objects/product#product-compare_at_price

This logic is a bit flawed, because, lowest price can be on one variant and lowest compare_at_price can be on another variant.

One would expect that if there is a compare_at_price on one variant, then this variant price should be even lower.
However, Shopify does not prevent you from entering compare_at_price lower than price on one of your variants and in this case there will be a false positive.

For the same reason, your savings math is flawed – it will take price from the cheapest variant but compare_at_price can come from another variant.

For this reason, code from @MARKLOIS is a more precise approach, kinda like this:

{%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
  {% for variant in product.variants %}
    {% if variant.compare_at_price > variant.price %}
      
          Save {{ card_product.compare_at_price | minus: card_product.price | money  -}}
      
      {% break %}
    {% endif %}
  {% endfor %}
{%- endif -%}

Often this was a problem for people who have a very cheap sampler as variant on product with other, full-priced variants – your code will display unreal savings in this case.

Hey @CHS-Steve ,

To show the sale badge if ANY variant is on sale, you’ll need to modify your code to check all product variants. Here’s a solution:

<div class="card__badge {{ settings.badge_position }}">
  {%- if card_product.available == false -%}
    <span
      id="NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
      class="badge badge--bottom-left color-{{ settings.sold_out_badge_color_scheme }}"
    >
      {{- 'products.product.sold_out' | t -}}
    </span>
  {%- else -%}
    {%- assign has_sale_variant = false -%}
    {%- for variant in card_product.variants -%}
      {%- if variant.compare_at_price > variant.price and variant.available -%}
        {%- assign has_sale_variant = true -%}
        {%- break -%}
      {%- endif -%}
    {%- endfor -%}

    {%- if has_sale_variant -%}
      <span
        id="NoMediaStandardBadge-{{ section_id }}-{{ card_product.id }}"
        class="badge badge--bottom-left color-{{ settings.sale_badge_color_scheme }}"
      >
        ON SALE      </span>
    {%- endif -%}
  {%- endif -%}
</div>

This code:

  1. Sets a variable has_sale_variant to false by default
  2. Loops through all product variants
  3. If any variant has a compare-at price greater than its actual price AND is available, sets has_sale_variant to true and breaks out of the loop
  4. Shows the sale badge if has_sale_variant is true

This should display the “ON SALE” badge on your product card as long as at least one variant is on sale, which is exactly what you’re looking for with your thermal imager product.

Feel free to reach out if you’ve any more questions for us, cheers!
Shubham | Untechnickle