Tutorial

How to add a sale badge in Shopify with compare_at_price

Show a "Sale" badge (or a percentage off) on discounted products in Shopify using compare_at_price. Here's the Liquid, how to calculate the discount percentage, and how to handle products with multiple variants correctly.

Bas Lefeber

Founder, learnshopify.dev

Published
Reading time
2 min read
Don't just read it, build it liveThis feature is a hands-on lesson in a live storefront, right in your browser. Free, nothing to install.

A sale badge is a small "Sale" or "-20%" flag on a product that's discounted. In Shopify, "on sale" has a precise meaning: the product has a compare_at_price that's higher than its current price. That comparison is the whole feature.

A sale badge plus the original price struck through.

The basic sale badge

snippets/product-card.liquid
{% if product.compare_at_price > product.price %}  <span class="badge badge--sale">Sale</span>{% endif %}

If there's no compare-at price, product.compare_at_price is nil, the comparison is false, and no badge renders. Exactly what you want.

The whole feature is one comparison: compare_at_price > price decides whether the badge renders.

Showing the percentage off

A specific number converts better than a generic "Sale." Compute the discount as a percentage. Remember both prices are in cents, but since it's a ratio the units cancel:

liquid
{% if product.compare_at_price > product.price %}  {% assign saved = product.compare_at_price | minus: product.price %}  {% assign pct = saved | times: 100 | divided_by: product.compare_at_price %}  <span class="badge badge--sale">-{{ pct }}%</span>{% endif %}

Filters can't go inside the comparison

You can't write if pct > saved | times: 100, Liquid won't apply a filter inside a comparison operand. Always assign the computed value to a variable first, then compare or output it. This trips up almost everyone (and most AI suggestions).

Products with multiple variants

On a product card the bare product.compare_at_price reflects the first variant. If only some variants are discounted, use the price-range fields so the badge reflects whether any variant is on sale:

liquid
{% if product.compare_at_price_max > product.price_min %}  <span class="badge badge--sale">Sale</span>{% endif %}

Tip

Pair the badge with a struck-through original price for a stronger signal: <s>{{ product.compare_at_price | money }}</s> {{ product.price | money }}.

What AI tools get wrong here

Two recurring mistakes: trying to apply a filter inside the if comparison (Liquid rejects it), and using the single-variant compare_at_price on a multi-variant product so the badge misfires. Knowing to assign first and to reach for the _max/_min range fields on cards is the production-grade version.

Learn this properly · free lesson

Add a Sale badge to discounted items

Build the sale badge in the interactive editor against a real product, including the multi-variant edge case. Free lesson, try it without signing up.

Try this lesson for free

Frequently asked questions

What is compare_at_price in Shopify?

It's the product's original (pre-discount) price. When compare_at_price is set and higher than price, the product is on sale. Merchants set it on the variant in the product admin.

How do I calculate the discount percentage in Liquid?

Assign saved = compare_at_price minus price, then pct = saved times 100 divided_by compare_at_price. You must assign the computed value first, Liquid won't apply filters inside an if comparison.

Why does my sale badge show on the wrong products?

Usually because you're comparing a single variant's compare_at_price on a multi-variant product. On product cards, use compare_at_price_max and price_min so the badge reflects whether any variant is discounted.

How do I show the original price struck through?

Wrap the compare-at price in an <s> tag and format both with the money filter: <s>{{ product.compare_at_price | money }}</s> {{ product.price | money }}.

  • Liquid
  • Pricing
  • Product cards

About the author

Bas Lefeber, Founder, learnshopify.dev

Bas builds learnshopify.dev, where developers learn production-grade Shopify theme development against a live storefront. He writes about Liquid, theme architecture, and the parts of the job that still matter now that AI writes the code.

Keep going in the curriculum

Start free

Ready to become a Shopify developer?

You just read how it works. Now write it yourself: real tickets from a live store, in an editor where the storefront updates as you type. Module 1 is free, no card.

Start your first lesson

Free · No credit card · Your first win in minutes