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:
- 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."
- 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."
- Locate
price.liquid: In the file search bar, typeSnippetsto open the folder, then find and click onprice.liquid. - Replace Content: Delete the entire content of the
price.liquidfile. - Paste the New Code: Copy and paste the following code provided by Mustafa_Ali into the
price.liquidfile:
{%- 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 }}
- Save Changes: Click the "Save" button in the top right corner.
- 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.