Skip to content

Shopify Dude Complete Guide

How to Build a Custom Sale Price in Shopify Without Changing compare_at_price

A practical Shopify architecture for custom sale pricing using metafields, theme display logic, and Shopify Functions without repurposing compare-at price.

Quick answer

You can build a Shopify sale-price system without changing compare_at_price by separating the job into three layers: store the promotional price in custom data, display it consistently in the theme, and use a Shopify Discount Function to enforce the actual discount in checkout.

The important part is not the metafield itself. It is keeping the storefront display and the checkout calculation tied to the same rules. If the theme says an item is $79 but Shopify still charges $99, the implementation is incomplete.

On this page

Why compare_at_price is not always the right tool

Shopify already has a native compare-at price on each variant, and for many stores that is exactly what should be used. It is simple, understood by themes, and works naturally with normal sale presentation.

But there are stores where compare-at price is already carrying another merchandising meaning, where a temporary promotion should not rewrite catalog pricing, or where the business needs a separate promotional control. In those cases, a custom sale field can be cleaner than repurposing the native price model.

The architecture is three separate jobs

1. Store the rule

A common pattern is a product-level sale switch and price, with an optional variant-level override. For example:

  • Product sale active: whether the custom promotion is enabled.
  • Product sale price: the default promotional price.
  • Variant sale price: an optional override when one variant needs a different amount.

The exact field names do not matter nearly as much as the resolution rule. Decide which value wins before you write storefront code. A predictable rule is: variant override first, product sale price second, normal Shopify price otherwise.

2. Display the price everywhere the buyer sees it

The theme needs to resolve the same price on the product page, collection cards, quick add, cart drawer, full cart, recommendation blocks, and any third-party surface that renders price independently.

This is where custom pricing projects often become inconsistent. The PDP is changed first, but the cart drawer still reads the variant’s native price. Or product cards show the sale while a quick-add modal does not. Treat price rendering as a shared rule, not a one-page customization.

3. Enforce the price in checkout

Liquid and JavaScript can change what the buyer sees, but they do not change what Shopify is authoritative about charging. If a custom sale price needs to become the actual transaction price, enforce the rule with a platform pricing mechanism such as a Shopify Discount Function.

This division is useful: custom data decides eligibility and amount, the theme communicates the price, and the Function enforces it.

Do not calculate the discount backward in six different places

If the normal variant price is $100 and the custom sale price is $80, the business rule is “sell this eligible one-time line for $80.” The storefront can display $80 directly. The Function may need to express the result as a discount against the current line price, but that calculation should happen from the same source data.

Avoid maintaining separate percentages in the theme and app. Percentage math becomes especially fragile when base prices change, Markets are involved, or variants have different prices.

Product price vs. variant override

Variant overrides are useful, but they should be optional. If every variant has to duplicate the same sale price, the data model becomes harder to maintain than the promotion itself.

A practical resolution rule looks like this:

  1. If the sale is inactive, use normal Shopify pricing.
  2. If the selected variant has a valid custom sale price, use it.
  3. Otherwise use the product-level custom sale price.
  4. If the custom value is blank, zero, or not lower than the normal price, fail closed and use normal pricing.

Subscriptions need a separate decision

If a product can be purchased once or through a subscription app, do not assume the custom sale belongs to both purchase types. Selling plans already have their own pricing adjustments. Applying an additional custom sale indiscriminately can create an unintended double discount.

Decide explicitly whether the promotion applies to one-time purchases, subscriptions, or both. If it is one-time only, the checkout logic needs to exclude cart lines associated with a selling plan. The storefront should make the same distinction so the displayed price matches the selected purchase option.

Markets and currencies make display-only shortcuts riskier

International pricing can make hard-coded sale math fail quickly. Shopify exposes contextual pricing for variants, and the price that applies in one market may not be the same base amount used in another. If the promotion must work internationally, decide whether the custom value is a fixed amount, market-specific value, or a rule that derives from the contextual price.

Do not assume a USD metafield can simply be printed as the sale price in every market.

Third-party price surfaces still count

Apps such as subscriptions, upsells, recommendations, bundles, or cart replacements may render their own price UI. Changing a Liquid snippet does not automatically update those surfaces.

Inventory the places where price appears before calling the implementation complete. In a real store, that usually includes more than the product template.

Why the obvious solutions fail

  • Theme-only pricing: looks right, charges the wrong amount.
  • Function-only pricing: checkout is right, but the customer sees the wrong price until late in the funnel.
  • Changing compare-at price for every campaign: can overwrite merchandising data the business wanted to preserve.
  • Applying the rule to every cart line: can discount subscriptions or otherwise ineligible purchase types.
  • Hard-coding one currency: breaks as soon as pricing becomes market-aware.

Common misunderstanding

A custom sale price is not just a theme feature. If the custom amount is supposed to be the price the customer actually pays, the storefront and Shopify’s checkout calculation must agree. Styling a lower number on the PDP is not a pricing system.

How to test this

  • Test a product with only the product-level sale price.
  • Test a variant with its own override.
  • Test an inactive sale and an invalid sale price.
  • Compare PDP, collection card, quick add, cart drawer, cart, and checkout.
  • Test one-time and subscription purchase options separately.
  • Test a mixed cart containing sale and non-sale products.
  • Test at least one additional market or currency if the store sells internationally.
  • Confirm the final order records the amount the storefront promised.

Sources and further reading