Products
The commerce extension fills a product view page with the values for one product and with the collection widgets that surround it. Read a single field with a {product.*} token on the product view page and inside a product loop, and use the collection loops to render related, complementary, and recently viewed products.
Product fields
These tokens resolve on the product view page and inside any product collection loop.
| Token | Meaning | Example |
|---|---|---|
{product.id} | Numeric product id | <div data-product-id="{product.id}"> |
{product.slug} | Product URL slug | <div data-product-slug="{product.slug}"> |
{product.name} | Product name | <h1>{product.name}</h1> |
{product.price} | Formatted price | <span>{product.price}</span> |
{product.image_url} | Primary product image URL | <img src="{product.image_url}" alt="{product.name}"> |
{product.link} | URL of the product view page | <a href="{product.link}">{product.name}</a> |
{product.description_html} | Description rendered as HTML | <div class="prose">{product.description_html}</div> |
{product.product_type} | Product type label | <span>{product.product_type}</span> |
{product.overall_rating} | Average rating, one decimal | <span>{product.overall_rating}</span> |
{product.overall_rating_count} | Number of ratings | <span>{product.overall_rating_count} reviews</span> |
{product.overall_rating_stars} | Star string for the rating | <span>{product.overall_rating_stars}</span> |
{product.overall_rating_stars_html} | Star markup for the rating | <span>{product.overall_rating_stars_html}</span> |
{product.overall_rating_stars} renders a five character string such as ★★★★☆, while {product.overall_rating_stars_html} renders the same rating as markup you can style.
<div class="product">
<img src="{product.image_url}" alt="{product.name}">
<h1>{product.name}</h1>
<p class="price">{product.price}</p>
<p class="rating">
<span class="stars">{product.overall_rating_stars}</span>
<span>{product.overall_rating} ({product.overall_rating_count})</span>
</p>
<a href="{product.link}">View product</a>
{if product.description_html}
<div class="prose">{product.description_html}</div>
{/if}
</div>
{product.image_url} covers the primary image. A modern AVIF source is exposed on commerce line items instead: inside a cart.items or checkout.items loop, {item.image} is the standard source and {item.image_avif} is the AVIF variant, so you can pair them in a <picture>.
Structured data
Richer fields are stored as JSON. Embed them in a <script type="application/json"> block and read the payload from JavaScript, or output the value where raw JSON is expected.
| Token | Holds |
|---|---|
{product.attributes_json} | Product attributes array |
{product.media_json} | Product media array |
{product.variants_json} | Product variants array |
{product.specifications_json} | Specifications shown on the product page |
{product.virtual_services_json} | Virtual services array |
{product.downloads_json} | Downloadable file entries with size, checksum, and bundle fields; the stored url is configuration, not a buyer link |
{product.group_items_json} | Grouped product items |
{product.meta_json} | Product metadata object |
<script id="product-media" type="application/json">{product.media_json}</script>
<script id="product-variants" type="application/json">{product.variants_json}</script>
<script id="product-attributes" type="application/json">{product.attributes_json}</script>
For how configurable products, virtual products, and downloadable delivery are configured, including entitlement-gated downloads, uploaded files, ZIP bundles, per-variation overrides, and unlimited quantity, see Variable and digital products.
Product collection loops
A product view page exposes five ranked collections. Render each one with {each <collection> as=product}. The alias defaults to product, and limit=<n> caps how many items render. Sections on the same page do not repeat a product that an earlier section already showed.
| Collection | What it yields | Example opening tag |
|---|---|---|
related_products | Products related to the current product | {each related_products as=product limit=4} |
frequently_bought | Products often bought together with it | {each frequently_bought as=product limit=4} |
category_best_sellers | Best sellers in the product's category | {each category_best_sellers as=product limit=4} |
also_viewed_products | Products other shoppers also viewed | {each also_viewed_products as=product limit=4} |
browsing_history_products | Products the visitor viewed recently | {each browsing_history_products as=product limit=4} |
limit defaults to 12 and accepts up to 200 items.
Inside a loop the same {product.*} fields are available, so a card can show the image, name, price, and link.
{if related_products}
<section>
<h2>Related products</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
{each related_products as=product limit=4}
<a href="{product.link}" class="block">
<img src="{product.image_url}" alt="{product.name}">
<h3>{product.name}</h3>
<span>{product.price}</span>
</a>
{/each related_products}
</div>
</section>
{/if}
The same markup can reuse a shared product card block, so the card lives in one place:
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
{each frequently_bought as=product limit=4}
{block_ref code=product_item}
{/each frequently_bought}
</div>
Category grids
{each categories as=category} iterates the storefront's categories. It is available to page templates, including the product view page, so you can build category navigation alongside a product.
| Token | Meaning | Example |
|---|---|---|
{category.name} | Category name | <span>{category.name}</span> |
{category.url} | URL of the category view page | <a href="{category.url}">{category.name}</a> |
{category.image} | Category image URL | <img src="{category.image}" alt="{category.name}"> |
{each categories as=category limit=6}
<a href="{category.url}" class="block">
<img src="{category.image}" alt="{category.name}">
<span>{category.name}</span>
</a>
{/each categories}
The alias defaults to category. Inside the loop you can also read {category.slug}, {category.product_count}, and {category.is_current}.

