Building a Complete Product Section
A production product page coordinates media gallery, variant selection, price updates, inventory messaging, and add-to-cart — all in one Online Store 2.0 section. This example shows the Liquid foundation before JavaScript enhancement.
Full product section with media and variants
{%- liquid
assign current_variant = product.selected_or_first_available_variant
assign featured_media = current_variant.featured_media | default: product.featured_media
-%}
<div
class="product"
id="Product-{{ section.id }}"
data-section-id="{{ section.id }}"
data-product-id="{{ product.id }}"
>
<div class="product__media">
{% if featured_media %}
{{ featured_media
| image_url: width: 1200
| image_tag: loading: 'eager', alt: product.title, id: 'ProductImage'
}}
{% endif %}
<div class="product__thumbnails">
{% for media in product.media %}
<button type="button" data-media-id="{{ media.id }}">
{{ media | image_url: width: 120 | image_tag: alt: media.alt }}
</button>
{% endfor %}
</div>
</div>
<div class="product__info">
<h1>{{ product.title }}</h1>
<p class="product__vendor">{{ product.vendor }}</p>
<div class="product__price" id="Price-{{ section.id }}">
{% if current_variant.compare_at_price > current_variant.price %}
<s>{{ current_variant.compare_at_price | money }}</s>
{% endif %}
<span>{{ current_variant.price | money }}</span>
</div>
{% form 'product', product, id: 'product-form' %}
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<fieldset>
<legend>{{ option.name }}</legend>
{% for value in option.values %}
<input
type="radio"
name="options[{{ option.name | escape }}]"
value="{{ value | escape }}"
{% if option.selected_value == value %}checked{% endif %}
>
{{ value }}
{% endfor %}
</fieldset>
{% endfor %}
{% endunless %}
<input type="hidden" name="id" value="{{ current_variant.id }}">
<button
type="submit"
name="add"
class="btn"
{% unless current_variant.available %}disabled{% endunless %}
>
{% if current_variant.available %}
{{ 'products.add_to_cart' | t }}
{% else %}
{{ 'products.sold_out' | t }}
{% endif %}
</button>
{% endform %}
<div class="rte product__description">{{ product.description }}</div>
</div>
</div>
<script type="application/json" id="ProductJson-{{ product.id }}">
{{ product | json }}
</script>- options_with_values — OS 2.0 API for variant pickers with selected state
- Hidden input name="id" must update when variant changes (JavaScript)
- Embed product JSON for client-side variant resolution
- loading: 'eager' on hero image — LCP optimization
- section.id in element IDs — supports multiple product sections
Note: For 100+ variant products, use a JavaScript variant picker library or Shopify's reference Dawn implementation.