What you will verify
You will distinguish product-level facts from sellable variant facts and render a small inspectable fact block that stays synchronized with the selected variant.
Rhode Glazing Milk illustrates the requirement: 4.2 oz and 1.7 oz sizes had separate $32 and $20 offers. Treating the first offer as the whole product would answer a size-specific question incorrectly.
Product facts versus variant facts
| Product-level | Variant-level |
|---|---|
| title | SKU |
| vendor or brand context | barcode |
| description | option values |
| product type and category | price and compare-at price |
| shared ingredients | availability |
| shared compatibility | inventory-linked state |
A bundle may need a third layer for included items.
Exact Shopify Admin path
Online Store → Themes → duplicate live theme → Edit code
Open the main product section used by the product template. In current theme structures this is often under sections/, but the exact filename varies.
Resolve the current variant
{% assign current_variant = product.selected_or_first_available_variant %}
<dl class="product-facts" data-product-id="{{ product.id }}">
<div>
<dt>Product</dt>
<dd>{{ product.title | escape }}</dd>
</div>
<div>
<dt>Variant</dt>
<dd>
{% if current_variant.title == 'Default Title' %}
Standard
{% else %}
{{ current_variant.title | escape }}
{% endif %}
</dd>
</div>
{% if current_variant.sku != blank %}
<div>
<dt>SKU</dt>
<dd>{{ current_variant.sku | escape }}</dd>
</div>
{% endif %}
<div>
<dt>Price</dt>
<dd>{{ current_variant.price | money_with_currency }}</dd>
</div>
<div>
<dt>Availability</dt>
<dd>{% if current_variant.available %}In stock{% else %}Sold out{% endif %}</dd>
</div>
</dl>
This is the initial server-rendered state. A variant picker must update the same visible values after selection.
Expose a controlled variant payload
Do not serialize the entire product object into arbitrary attributes. Emit only fields the product UI needs.
<script type="application/json" data-product-variants>
[
{% for variant in product.variants %}
{
"id": {{ variant.id | json }},
"title": {{ variant.title | json }},
"sku": {{ variant.sku | json }},
"available": {{ variant.available | json }},
"price": {{ variant.price | json }},
"url": {{ variant.url | json }},
"options": {{ variant.options | json }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
</script>
Use a theme component to update price, SKU, availability, canonical decision, and add-to-cart controls from one selected variant.
Real failure patterns
Shared SKU across sizes
The observed Rhode entities used one SKU for two sizes. If Shopify variant records have unique SKUs, ensure the rendered graph reads variant.sku, not a product metafield copied to every variant.
Default Title in public name
The observed HexClad entity exposed Default Title. Suppress that label for a simple product without pretending that all products lack variants.
Offer range versus selected price
Puffy’s Product graph exposed a $449–$1,749 range. That supports discovery, but the selected Queen variant still needs its exact current price when the buyer asks for Queen.
Expected result
Change variants and confirm:
- option label changes
- SKU changes where expected
- price and currency change correctly
- availability and buy button agree
- URL or variant parameter remains recoverable
- structured offer is not stuck on the first variant
Verification
Test with JavaScript enabled and disabled. Save initial HTML for direct retrieval, then inspect the rendered DOM after switching variants.
Rollback
Keep changes in a duplicate theme. Restore the previous product section if variant picker, analytics, subscriptions, or add-to-cart behavior changes unexpectedly.
Community discussion
Add to the article
Ask a technical question, share a storefront result, or challenge a conclusion with evidence.