Shopify Variant Pricing Glitch: Displaying Available Prices on Collection Pages

Hey there, fellow store owners! It's your Shopify migration expert here, diving into a super common, yet incredibly frustrating, pricing quirk that often pops up on collection pages. You know the one: your product card shows a super low price, only for customers to click through and find that variant is sold out, or the actual available price is much higher. Talk about a bait-and-switch feeling!

This exact scenario recently came up in the Shopify community, and I thought it was a perfect example of how small code tweaks can make a huge difference in customer experience and conversions. Let's dig into a recent thread where a store owner, GamesRealm, tackled this head-on.

The Problem: Misleading Variant Prices on Collection Pages

GamesRealm runs an awesome store, Games Realm, specializing in Pokémon singles. They noticed a significant issue: on their collection pages, products like "Conkeldurr 65/101" were displaying the lowest variant price ($2 for a 'poor' condition card) even though that specific condition was out of stock. The highest available variant ('Excellent') was $2.40, but the collection page stubbornly showed the $2 price. This is a classic example of a theme defaulting to the absolute lowest price (product.price_min) without checking for variant availability.

GamesRealm had already tried a Shopify guide to edit their price.liquid file, but it hadn't solved the problem, leading them to seek help from the community.

The Community Steps In: Finding an Available Price

Mustafa_Ali, a helpful community member, quickly jumped in to offer a solution. The core idea was to modify the price.liquid snippet to explicitly look for the cheapest available variant rather than just the lowest-priced one overall. This ensures that the price displayed on collection cards accurately reflects what a customer can actually purchase.

Here's the problem code GamesRealm initially had in their price.liquid (or similar logic):

{%- liquid
     if use_variant
        assign target = product.selected_or_first_available_variant
    else
        assign target = product
    endif

    assign compare_at_price = target.compare_at_price
    assign price = target.price | default: 1999
    assign available = target.available | default: false

This snippet, while seemingly logical, often doesn't differentiate between an available variant's lowest price and an unavailable variant's lowest price, leading to the issue GamesRealm faced.

The Solution: Prioritizing Available Variants

Mustafa_Ali provided a comprehensive update to the price.liquid file. This revised code introduces a critical step: it first filters all product variants to find only those that are available, then sorts them by price to pick the first (i.e., cheapest) one. Only if no variants are available does it fall back to the generic target.price.

Here are the steps to implement Mustafa's solution:

  1. Backup Your Theme! Seriously, always do this before editing any code. Go to your Shopify Admin → Online Store → Themes. Find your current theme, click "Actions" → "Duplicate."
  2. Access Theme Code: In your Shopify Admin, go to Online Store → Themes. Click "Actions" next to your live theme (or the duplicated theme you're working on) and select "Edit code."
  3. Locate price.liquid: In the file search bar, type Snippets to open the folder, then find and click on price.liquid.
  4. Replace Content: Delete the entire content of the price.liquid file.
  5. Paste the New Code: Copy and paste the following code provided by Mustafa_Ali into the price.liquid file:
{%- comment -%}
    Renders a list of product's price (regular, sale)

    Accepts:
    - product: {Object} Product Liquid object (optional)
    - use_variant: {Boolean} Renders selected or first variant price instead of overall product pricing (optional)
    - show_badges: {Boolean} Renders 'Sale' and 'Sold Out' tags if the product matches the condition (optional)
    - price_class: {String} Adds a price class to the price element (optional)

    Usage:
    {% render 'price', product: product %}
{%- endcomment -%}

{%- liquid
    if use_variant
        assign target = product.selected_or_first_available_variant
    else
        assign target = product
    endif

    comment
      FIX: Instead of using product.price (which is price_min and can include
      unavailable variants), we find the cheapest AVAILABLE variant explicitly.
      This ensures collection pages show the correct in-stock starting price.
    endcomment

    assign available_variants = product.variants | where: 'available', true

    if available_variants.size > 0
        assign first_available = available_variants | sort: 'price' | first
        assign compare_at_price = first_available.compare_at_price
        assign price = first_available.price
        assign available = true
    else
        assign compare_at_price = target.compare_at_price
        assign price = target.price | default: 1999
        assign available = target.available | default: false
    endif

    if settings.currency_format_enable
        assign m | money_with_currency
    else
        assign m | money
    endif

    if target == product and product.price_varies
        assign m | t: price: money_price
    endif
-%}

{%- comment -%} Explanation of description list: - div.price__regular: Displayed when there are no variants on sale - div.price__sale: Displayed when a variant is on sale - div.price__availability: Displayed when the product is sold out {%- endcomment -%}
{{ money_price }}
{%- if settings.currency_format_enable -%} {{ compare_at_price | money_with_currency }} {%- else -%} {{ compare_at_price | money }} {%- endif -%}
{{ money_price }}
{%- if settings.show_saved_price and price != nil and price != blank and compare_at_price != nil and compare_at_price != blank -%} {%- assign saved_price = compare_at_price | minus: price | money -%}
{{ 'products.product.price.saved_price_html' | t: price: saved_price }}
{%- endif -%} {%- if settings.show_price_percent -%}
{%- liquid assign list_compare = product.variants | where: 'compare_at_price' assign compare = 0 for variant in list_compare assign saving = variant.compare_at_price | minus: variant.price | times: 100.0 | divided_by: variant.compare_at_price | round if saving > compare assign compare = saving endif endfor if compare < 1 assign compare = product.compare_at_price_max | minus: product.price_max | times: 100.0 | divided_by: product.compare_at_price_max | round endif -%} (-{{ compare | append: '%'}})
{%- endif -%}
{{ 'products.product.price.unit_price' | t }}
{{- product.selected_or_first_available_variant.unit_price | money -}}  {{ 'accessibility.unit_price_separator' | t }}  {%- if product.selected_or_first_available_variant.unit_price_measurement.reference_value != 1 -%} {{- product.selected_or_first_available_variant.unit_price_measurement.reference_value -}} {%- endif -%} {{ product.selected_or_first_available_variant.unit_price_measurement.reference_unit }}
  1. Save Changes: Click the "Save" button in the top right corner.
  2. Test Your Store: Clear your cache and check your collection pages to ensure the prices are now displaying correctly, showing the cheapest available variant price.

GamesRealm confirmed that Mustafa's solution worked perfectly for them, getting "everything showing correctly!"

An Important Nuance from tim_1: Product Page vs. Collection Page Logic

While Mustafa's solution directly addressed GamesRealm's issue, another community member, tim_1, brought up a crucial point: theme snippets like price.liquid are often used in multiple places, not just collection pages. He warned that changes made for collection pages might not work properly (or might have unintended side effects) on product pages if the same snippet is used there.

Tim_1 suggested an alternative approach if your goal is to always show the price of the first available variant (which might not be the cheapest, but is guaranteed to be in stock and selectable by default) rather than the absolute cheapest available variant. His proposed snippet modification looked like this:

{%- liquid

    assign price  = product.selected_or_first_available_variant.price | default: 1999
    assign compare_at_price = product.selected_or_first_available_variant.compare_at_price

    if use_variant
        assign target = product.selected_or_first_available_variant
    else
        assign target = product
    endif

This snippet directly assigns price and compare_at_price based on product.selected_or_first_available_variant. This is a simpler approach if you prefer to display the price of the variant that Shopify would automatically select (usually the first one in the list that's in stock) rather than explicitly finding the lowest available price. It's a subtle but important distinction depending on your store's specific pricing strategy and how you want to guide customers.

The key takeaway from tim_1's comment is always to be mindful of where a snippet is used in your theme. If you're making a change primarily for collection pages, ensure it doesn't break functionality or display on product pages, quick view modals, or other areas that might leverage the same code.

Wrapping Up

This discussion highlights a common challenge in Shopify theme customization: getting variant pricing to display exactly as you intend, especially when dealing with availability. The community's collaborative spirit, with Mustafa_Ali providing a direct working solution and tim_1 offering valuable contextual advice, really shows the power of shared knowledge.

Remember, when diving into theme code, always start with a theme backup. It's your best friend! And don't hesitate to leverage the Shopify community – there's a wealth of expertise out there ready to help you fine-tune your store for the best possible customer experience.

Share:

Use cases

Explore use cases

Agencies, store owners, enterprise — find the migration path that fits.

Explore use cases