Skip to content

Product forms and variants

{%- assign current = product.selected_or_first_available_variant -%}
{%- form 'product', product, id: 'ProductForm', class: 'product-form' -%}
<input type="hidden" name="id" value="{{ current.id }}">
<label for="Quantity">{{ 'products.product.quantity' | t }}</label>
<input id="Quantity" type="number" name="quantity" value="1" min="1">
<button type="submit" name="add" {% unless current.available %}disabled{% endunless %}>
{%- if current.available -%}{{ 'products.product.add_to_cart' | t }}{%- else -%}{{ 'products.product.sold_out' | t }}{%- endif -%}
</button>
{%- endform -%}

{% form 'product' %} posts to /cart/add and redirects to the cart. With JavaScript, intercept the submit and post the form data to /cart/add.js instead (see the cart API). Extra inputs named properties[Name] become line properties.

Draw one control per option from product.options_with_values:

{%- for option in product.options_with_values -%}
<fieldset>
<legend>{{ option.name }}</legend>
{%- for value in option.values -%}
<label>
<input type="radio" name="option{{ option.position }}" value="{{ value.name }}" {% if value.selected %}checked{% endif %} {% unless value.available %}data-unavailable{% endunless %}>
{{ value.name }}
</label>
{%- endfor -%}
</fieldset>
{%- endfor -%}
<script type="application/json" data-product-json>{{ product | json }}</script>

When the shopper picks values, find the variant in the product JSON whose options match, then:

  1. set the hidden id input to its ID;
  2. update the URL with ?variant=<id> (history.replaceState), so the page can be shared and reloaded;
  3. refresh price, stock and images: re-render the main section with ?variant=<id>&section_id=template--main, or update them from the JSON.

product.selected_variant is the variant from ?variant= (nil without it); selected_or_first_available_variant always has one.

variant.available says whether it can be bought. Exact stock isn’t exposed to themes; with the Stock status feature on, Sahil’s inventory-status block shows a low-stock message.

Gift card products have product.gift_card?. With the gift cards feature on, add recipient fields to the form (properties[Recipient email], properties[Recipient name], properties[Message] and properties[Send on]), as Sahil’s snippets/gift-card-recipient.liquid does.

Was this page helpful?