Personalized Products Guide
Some products need customer input at the time of purchase — an engraving on a mug, a monogram on a bag, a gift message on a card. This information is captured as line item properties: name and value pairs attached to a cart line rather than to the product itself. No variant, SKU, or inventory is required for each possible value.
Add Property Inputs to the Product Template
Property inputs are named properties[<name>], where <name> is the label stored with the line. Add them inside the add-to-cart form alongside the fields generated by the cart_form tag.
The engraving label and input are the only addition to the add-to-cart form.
{% purchase_info_for_product request product as session %}
{% if session.availability.is_available_to_buy %}
{% cart_form request product 'single' as cart_form %}
<form id="add-to-cart" action="{% url 'cart:add' pk=product.pk %}" method="post">
{% csrf_token %}
{% include "partials/form_fields.html" with form=cart_form %}
<label for="engraving">Engraving</label>
<input type="text" id="engraving" name="properties[Engraving]">
<button type="submit">{% t "store.catalogue.add_to_cart" %}</button>
</form>
{% else %}
{% t "store.catalogue.out_of_stock" %}
{% endif %}Add one input per property. A mug with an engraving and a font choice uses properties[Engraving] and properties[Font].
Display Properties in the Cart
The cart template receives a formset of cart line forms. Each form.instance is a line with a properties list of key and value pairs.
{% for property in properties %}
{% if property.value %}
<div>{{ property.key }}: {{ property.value }}</div>
{% endif %}
{% endfor %}{% for form in formset %}
{% with line=form.instance %}
{% include "partials/cart_line_properties.html" with properties=line.properties %}
{% endwith %}
{% endfor %}Themes that ship their own side cart JavaScript need to render properties there too. Request properties { key value } on the cart lines in your side cart query and output them alongside the product title.
Behavior to Expect
| Behavior | Detail |
|---|---|
| Unique lines | The same product added with different property values creates a separate cart line for each combination. Adding it again with identical values increases the quantity of the existing line. |
| Empty values | A property submitted with an empty value does not create a separate line. It is still recorded on the line, so guard your display with {% if property.value %}. |
| Hidden properties | Property names starting with an underscore, such as properties[_source], are stored but excluded from line.properties and from the Storefront GraphQL API. Use them for data that should not be shown to customers. |
| Value length | Values longer than 500 characters are truncated to 500 characters. |
| Checkboxes | When a checkbox named properties[...] changes, the value submitted is yes or no. |
| File uploads | File inputs are not supported. Inputs with type="file" are ignored. |
| Set once | Properties are captured when the line is added to the cart. There is no update path for changing them afterwards. |
Add Properties with the Storefront GraphQL API
Themes that add to the cart through the Storefront GraphQL API pass properties on each line as a JSON object of name and value pairs. The field is available on createCart and addCartLines, and is returned on cart lines as properties { key value }.
{
"input": {
"cartId": "<cart-id>",
"lines": [
{
"productPk": 1,
"quantity": 1,
"properties": { "Engraving": "Alex" }
}
]
}
}properties must be a JSON object. Any other value returns the error Properties must be a JSON object.
Related
line Object
Cart line object and the properties list available in templates.
cart_form Tag
Generate the add-to-cart form for a product template.
Product Variants Guide
Map variant attribute choices to product IDs when inventory is tracked per option.
Storefront GraphQL API
Add lines to the cart from JavaScript with the createCart and addCartLines mutations.