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 selectionnamefor display overridesminQtyfor minimum quantity rulescustom[...]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
Pricing Helpers (Recommended)
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
- add-to-cart form includes required hidden inputs
- CSRF is present and not cached incorrectly
- pricing uses helper methods instead of custom arithmetic
- configurable/custom payload keys are validated
- product unavailable states are handled before rendering CTA