cartItems, item properties, and data attributes for quantity and remove. For checkout-only topics (totals, form fields, coupon, payment), see Checkout.
Scope: cartItems / items
cartItems (or items) is an array of line items. The cart plugin fills it; UI updates are reactive when cart scope changes.
| Item property | Type | Description | In template |
|---|---|---|---|
code | string | Product/cart line code | Use with qty/remove data attributes |
name | string | Product name | [[ item.name ]] |
image | string | Product image URL | [[ item.image ]] |
quantity | number | Quantity | [[ item.quantity ]] |
price | string | Unit price (formatted) | [[ item.price ]] — do not use formatPrice |
total | string | Line total (formatted) | [[ item.total ]] — do not use formatPrice |
retail_price | string | Original/retail price (formatted) | [[ item.retail_price ]] |
discount_total | string | Per-line savings (formatted) | [[ item.discount_total ]] |
currency | string | Currency code | [[ item.currency ]] |
is_subscription | boolean | true when this is a subscription product | [[ item.is_subscription ]] |
subscription | object | null | Subscription details (see below) — null for one-time products | [[ item.subscription.frequency ]] |
bonuses | array | Bonus products included with this item (see below) | template-foreach loop |
courses | array | Courses linked to this product (see below) | template-foreach loop |
price and total are already formatted. Use [[ item.price ]] and [[ item.total ]] as-is. See Functions for when to use formatPrice.bonuses, courses, is_subscription, and subscription are populated after a brief async fetch from the server when the cart loads. They are always present (empty array / false / null) so templates never error.Subscription details (item.subscription)
When item.is_subscription is true, item.subscription contains billing cadence and trial settings:
| Property | Type | Description |
|---|---|---|
frequency | number | How often the subscription bills (e.g. 3) |
frequency_unit | string | Unit for the frequency: "day", "week", "month", or "year" |
trial_days | number | Free trial length in days (0 = no trial) |
first_charge_free | boolean | true when the first billing cycle is free |
is_subscription and subscription fields are available on checkout scope for single-product checkout pages (e.g. [[ checkout.is_subscription ]], [[ checkout.subscription.frequency ]]).
Bonus products (item.bonuses)
Each cart item may include a bonuses array — products that are included free with that item (configured in the product catalog under Bonuses). Populated automatically from the /api/cart-products response after the cart loads.
Each element in item.bonuses has:
| Property | Description |
|---|---|
code | Bonus product code |
name | Bonus product name |
image | Bonus product image URL |
price | Price (formatted) — typically $0.00 for free bonuses |
retail_price | Retail value (formatted) — useful for “valued at” display |
original_price | Same as retail_price |
currency | Currency code |
item.bonuses is always an array — empty when the product has no bonuses configured.
Courses (item.courses)
When a product has linked courses, they are available as item.courses. Populated automatically from the /api/cart-products response after the cart loads.
Each element in item.courses has:
| Property | Description |
|---|---|
id | Course ID |
title | Course title |
slug | URL slug (used to link to the course page) |
description | Short description |
cover | Cover image URL |
instructor_name | Instructor name (if set) |
status | Publication status ("published", "draft", etc.) |
item.courses is always an array — empty when the product has no linked courses.
Quantity and remove (data attributes)
Use these so the cart plugin handles +/- and remove without custom script:| Attribute | Element | Purpose |
|---|---|---|
data-cart-qty-minus | Button | Decrease quantity |
data-cart-qty-plus | Button | Increase quantity |
data-cart-qty-remove | Button/link | Remove line (set qty to 0) |
data-code="[[ item.code ]]" | Same element or parent | So the plugin receives the actual product code |
data-disable-when-one | Minus button (optional) | Disable when item.quantity <= 1 |
<template-if data-condition="cartItems.length > 1"> so it only shows when there is more than one item (use > for > in data-condition).
For custom behavior, use @click with window.ef.addToCart(code, options?, quantity?), window.ef.setCartQuantity(code, quantity), or window.ef.removeFromCart(code, allowClear?) if the app exposes window.ef.
Order bumps (on checkout)
On checkout pages, bump cards use these data attributes so the bump-products plugin can toggle and style:| Attribute | Where | Purpose |
|---|---|---|
data-bump-code="[[ bump.code ]]" | Card/container | Plugin adds .selected / .bump-selected when selected |
data-bump-checkbox="[[ bump.code ]]" | Checkbox | Links checkbox to bump code |
data-bump-toggle="[[ bump.code ]]" | Label or clickable wrapper | Click toggles the checkbox |
name="bump" or name="bump[]", value="[[ bump.code ]]" | Checkbox | Form submit and plugin read selected codes |
checkout.bump_products, checkout.selectedBumpLines) and full offer vs added template pattern, see Checkout — Order bumps.
Summary
- Use
cartItemsand[[ item.* ]];item.priceanditem.totalare already formatted. item.is_subscription/item.subscription— billing cadence; also oncheckoutscope for single-product pages.item.bonuses— free bonus products linked to the item; always an array.item.courses— courses linked to the product; always an array.- Use data-cart-qty-minus/plus/remove and data-code for qty and remove.
- Bumps: use data-bump-* attributes; scope and patterns are in Checkout.