Skip to main content
Version: 1.0.0

Product Details

Product detail pages combine catalog content, pricing logic, and add-to-cart behavior. This page documents the required form contract and the helper functions developers should use for reliable implementation.

Minimum Add-to-Cart Contract

A product page must submit:

  • a valid action URL
  • CSRF token
  • product identifier (product = product.yuiId)
  • quantity
<form action="{{ craft.getYuiProductAddToAction(product) }}" method="post" enctype="multipart/form-data" accept-charset="UTF-8">
{{ csrfInput() }}
{{ hiddenInput('product', product.yuiId) }}
{{ hiddenInput('qty', 1) }}

<button type="submit" class="btn btn-primary">
{{ "Add to cart"|t('yui') }}
</button>
</form>

Optional Form Inputs

You can enrich add-to-cart payload with:

  • configuration[...] for configurable product selection
  • name for display overrides
  • minQty for minimum quantity rules
  • custom[...] for extra payload data

Example:

{{ hiddenInput('configuration[15]', 'variant-sku') }}
{{ hiddenInput('custom[color]', 'red') }}
{{ hiddenInput('custom[_priority]', 1) }}

Notes:

  • keys prefixed with _ are private-scope custom values
  • private values are stored for admin workflows and should not be shown in customer-facing UI by default

Use product helper methods instead of duplicating price logic in Twig:

  • getPrice(type, includedTax, cacheable)
  • getOriginalPrice(), getFinalPrice(), getSpecialPrice()
  • getCatalogPrice(), getHistoryPrice()
  • formatted variants (...Formatted)
  • isOnSale()

Example:

{% set final = product.getFinalPrice() %}
{% set original = product.getOriginalPrice() %}

{% if product.isOnSale() %}
<del>{{ product.getOriginalPriceFormatted() }}</del>
<strong>{{ product.getFinalPriceFormatted() }}</strong>
{% else %}
<strong>{{ product.getFinalPriceFormatted() }}</strong>
{% endif %}

Field-Type Embedded Products

When product data is embedded via Craft field type content, resolve a real product element before calling helper methods.

{% set product = craft.getProduct(entry) %}
{% if product %}
{{ product.getFinalPriceFormatted() }}
{% endif %}

craft.getProduct() reads the configured product field from the provided element (usually an Entry) and resolves the linked product by plugin-managed data. Always null-check the result to avoid hard failures when the field is empty or not configured.

Developer Checklist

  1. add-to-cart form includes required hidden inputs
  2. CSRF is present and not cached incorrectly
  3. pricing uses helper methods instead of custom arithmetic
  4. configurable/custom payload keys are validated
  5. product unavailable states are handled before rendering CTA