Next Commerce
Cart System

Cart Summary

The Cart Summary enhancer renders a reactive price breakdown block that stays in sync with the cart automatically. It supports a zero-config default layout and a fully custom template system with variable substitution, conditional CSS classes, and repeating list containers for discount lines and per-item breakdowns.

Activation

Add data-next-cart-summary to any container element. No other configuration is required — the enhancer subscribes to the cart store and re-renders whenever totals change.

Activation
<div data-next-cart-summary></div>

Default Layout

When no <template> child is present the enhancer renders a built-in layout:

  • Subtotal — always shown
  • Discounts — shown only when discounts > 0
  • Shipping — shows "Free" when shipping cost is zero
  • Tax — shown only when tax > 0
  • Total — always shown

Each row uses the CSS classes next-summary-row, next-summary-label, next-summary-value, and a row-specific modifier (next-row-subtotal, next-row-discounts, next-row-shipping, next-row-tax, next-row-total).


Custom Template

Place a <template> element as a direct child of the container. Its innerHTML becomes the custom layout. Scalar values are substituted with {variableName} placeholders.

Custom Template
<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="row"><span>Discounts</span><span>-{discounts}</span></div>
    <div class="row"><span>Shipping</span><span>{shipping}</span></div>
    <div class="row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

Template Variables

VariableDescription
{subtotal}Sum of all line item prices at their base (campaign) rates — before promotions, shipping, and tax
{total}Final amount due: subtotal + shipping + tax − discounts. Use this as the checkout total
{shipping}Shipping charge for this order; displays Free when no shipping cost applies
{shippingOriginal}Pre-discount shipping rate — only rendered when a shipping promotion is active, empty string otherwise. Pair with {shipping} to show the original price with a strike-through
{tax}Tax collected on this order (formatted currency)
{discounts}Combined monetary reduction from offer promotions and coupon codes
{savings}Total customer savings — the gap between full retail (compare-at) pricing and what the customer actually pays, including both sale pricing and applied promotions. Use this for a "You save" line
{compareTotal}What the cart would cost at full retail (compare-at) prices — empty if no compare-at prices are configured
{itemCount}Number of distinct package lines in the cart (not total units). Example: 2× Package A + 3× Package B → {itemCount} is 2

How the totals relate

  • {compareTotal} → full retail price
  • {subtotal} → after sale pricing, before promotions and shipping
  • {total}{subtotal} + {shipping} + {tax}{discounts}
  • {savings}{compareTotal}{total} (everything the customer didn't pay)

State CSS Classes

The enhancer adds and removes classes on the host element to reflect current cart state. Use these to show or hide rows with CSS instead of duplicating logic in templates.

ClassWhen applied
next-cart-emptyCart has no items
next-cart-has-itemsCart has one or more items
next-has-discountsDiscounts > 0
next-no-discountsDiscounts = 0
next-has-shippingShipping cost > 0
next-free-shippingShipping cost = 0
next-has-shipping-discountA shipping discount is applied
next-no-shipping-discountNo shipping discount
next-has-taxTax > 0
next-no-taxTax = 0
next-has-savingsRetail or discount savings are available
next-no-savingsNo savings

Hiding rows conditionally with CSS

Conditional Row Visibility
<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="row discount-row"><span>Discounts</span><span>-{discounts}</span></div>
    <div class="row shipping-row"><span>Shipping</span><span>{shipping}</span></div>
    <div class="row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

<style>
  .next-no-discounts .discount-row { display: none }
  .next-free-shipping .shipping-row { display: none }
</style>

Summary Lists

Inside a custom <template> you can include list containers with dedicated summary attributes. The enhancer populates these after each render.

Each list container must include its own <template> child that defines the markup for a single item.

Discount Lists

Use data-summary-offer-discounts or data-summary-voucher-discounts to render one row per applied discount.

Discount item variables:

VariableDescription
{discount.name}Discount / offer name
{discount.amount}Formatted discount amount
{discount.description}Optional description
Discount Lists
<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>

    <ul data-summary-offer-discounts>
      <template><li>{discount.name} — -{discount.amount}</li></template>
    </ul>

    <ul data-summary-voucher-discounts>
      <template><li>Coupon {discount.name}: -{discount.amount}</li></template>
    </ul>

    <div class="row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

Line Items List

Use data-summary-lines to render one row per cart line with full pricing detail sourced from the API summary.

Line Items List
<ul data-summary-lines>
  <template>
    <li>
      <img src="{line.image}" alt="{line.name}" />
      <span>{line.name}</span>
      <span>{line.qty} × {line.unitPrice}</span>
      <span>{line.total}</span>
    </li>
  </template>
</ul>

Line item variables:

VariableDescription
{line.packageId}Package ref_id
{line.quantity}Number of packages on this line (integer, from the API)
{line.qty}Same value as {line.quantity} — preferred alias for templates
{line.name}Package display name
{line.image}Product image URL
{line.productName}Product name
{line.variantName}Variant name (e.g. "Black / Small")
{line.sku}Product SKU

Base prices (campaign catalog) — before any runtime promotions:

VariableDescription
{line.price}Per-unit price as configured in the campaign
{line.priceTotal}{line.price} × quantity for this line
{line.priceRetail}Full retail (compare-at) price per unit
{line.priceRetailTotal}Retail price × quantity
{line.priceRecurring}Per-unit recurring billing price (subscriptions only)
{line.priceRecurringTotal}Recurring price × quantity (subscriptions only)
{line.isRecurring}"true" if this line bills on a recurring schedule

Checkout prices (after promotions) — values from the live cart calculation:

VariableDescription
{line.unitPrice}Per-unit price after all discounts
{line.originalUnitPrice}Per-unit price before discounts — pair with {line.unitPrice} for before/after display
{line.packagePrice}Line total after discounts (unit price × quantity)
{line.originalPackagePrice}Line total before discounts
{line.subtotal}Line subtotal as calculated by the API
{line.totalDiscount}Total discount applied to this line
{line.total}Final line amount after all discounts

Conditional helpers — resolve to "show" or "hide" for use as CSS class names:

VariableValuesDescription
{line.hasDiscount}"show" / "hide""show" when any promotion reduces this line's price
{line.hasSavings}"show" / "hide""show" when the unit price is below the retail (compare-at) price
Conditional Helpers
<!-- Show a "Sale" badge only when a discount is applied to this line -->
<span class="badge {line.hasDiscount}">Sale</span>
.hide { display: none; }

Per-Line Discount List

Each line item can have its own list of individual discounts. Place a data-line-discounts container inside the data-summary-lines item template:

Per-Line Discounts
<ul data-summary-lines>
  <template>
    <li class="line-item">
      <span>{line.quantity}× {line.name}</span>
      <span>{line.total}</span>

      <ul data-line-discounts>
        <template>
          <li class="line-discount">{discount.name} — -{discount.amount}</li>
        </template>
      </ul>
    </li>
  </template>
</ul>

Line discount variables:

VariableDescription
{discount.name}Offer name applied to this line
{discount.amount}Formatted discount amount
{discount.description}Optional description

data-line-discounts also receives next-summary-empty / next-summary-has-items — use .next-summary-empty { display: none } to hide it when no discounts apply to the line.

List State Classes

ClassWhen
next-summary-emptyNo items in the list
next-summary-has-itemsOne or more items in the list

Examples

Minimal — default layout

Minimal
<div data-next-cart-summary></div>

Custom template with conditional discount row

Conditional Discount Row
<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="row discount-row"><span>Discounts</span><span>-{discounts}</span></div>
    <div class="row"><span>Shipping</span><span>{shipping}</span></div>
    <div class="row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

<style>
  .next-no-discounts .discount-row { display: none }
</style>

Full breakdown with per-item lines and discount lists

Full Breakdown
<div data-next-cart-summary>
  <template>
    <!-- Per-item breakdown -->
    <ul class="line-items" data-summary-lines>
      <template>
        <li class="line-item">
          <img src="{line.image}" alt="{line.name}" />
          <div>
            <strong>{line.name}</strong>
            <span>{line.variantName}</span>
          </div>
          <div class="prices">
            <s class="{line.hasSavings}">{line.priceRetail}</s>
            <span>{line.unitPrice} × {line.qty}</span>
            <strong>{line.total}</strong>
          </div>
        </li>
      </template>
    </ul>

    <!-- Totals -->
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>

    <!-- Offer discounts -->
    <ul class="discount-list" data-summary-offer-discounts>
      <template>
        <li class="discount-item">{discount.name} — -{discount.amount}</li>
      </template>
    </ul>

    <!-- Voucher discounts -->
    <ul class="discount-list" data-summary-voucher-discounts>
      <template>
        <li class="discount-item">Coupon {discount.name}: -{discount.amount}</li>
      </template>
    </ul>

    <div class="row shipping-row">
      <span>Shipping</span>
      <span>{shipping}</span>
    </div>
    <div class="row savings-row">
      <span>You save</span>
      <span>{savings}</span>
    </div>
    <div class="row total-row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

<style>
  .next-free-shipping .shipping-row { display: none }
  .next-no-savings .savings-row { display: none }
</style>

Shipping discount with strikethrough

Shipping Discount
<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="row">
      <span>Shipping</span>
      <span>
        <s class="original-shipping">{shippingOriginal}</s>
        {shipping}
      </span>
    </div>
    <div class="row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

<style>
  /* Hide strikethrough when no shipping discount is active */
  .next-no-shipping-discount .original-shipping { display: none }
</style>

Cart Display Attributes Reference

Use data-next-display on any element to bind it to a live cart value.

Cart Status

Attribute valueDescriptionExample output
cart.isEmptyCart has no items"true" / "false"
cart.hasItemsCart has at least one item"true" / "false"
cart.itemCountNumber of distinct line items"3"
cart.quantityTotal quantity across all items"5"
cart.countAlias for cart.quantity"5"
cart.currencyActive currency for the cart"USD"
cart.discountCodeFirst/single discount code"SAVE10"
cart.discountCodesAll discount codes"SAVE10, EXTRA"
cart.couponCountNumber of coupons applied"1"

Cart Financial Totals

Attribute valueDescriptionExample output
cart.subtotalSubtotal (before shipping/tax)"$89.99"
cart.subtotal + data-include-discountsSubtotal after discount codes"$80.99"
cart.totalCart total"$99.99"
cart.totalExclShippingTotal excluding shipping"$94.99"
cart.shippingShipping cost"$5.00"
cart.discountsDiscount amount from coupons"-$9.00"
cart.savingsAmountSavings (retail only, no coupons)"$20.00"
cart.savingsPercentageSavings % (retail only)"25%"
cart.totalSavingsAmountTotal savings (retail + coupons)"$29.00"
cart.totalSavingsPercentageTotal savings %"30%"
cart.compareTotalCompare-at total"$119.99"

Raw Values (for calculations)

Append .raw for unformatted numbers. Use in expressions or with data-format.

Attribute valueDescription
cart.subtotal.rawNumeric subtotal
cart.total.rawNumeric total
cart.totalExclShipping.rawNumeric total excluding shipping
cart.shipping.rawNumeric shipping
cart.discounts.rawNumeric discount amount
cart.savingsAmount.rawNumeric savings
cart.compareTotal.rawNumeric compare total

Per-Item Quantity

Attribute valueDescriptionExample
cart.items[2].quantityQuantity of package ID 2 in cart"2"

Cart Conditional Attributes

Show or hide blocks based on cart state using data-next-show or data-next-hide.

ExpressionWhen true
cart.isEmptyCart has no items
cart.hasItemsCart has at least one item
cart.hasItem(2)Package ID 2 is in cart
cart.hasSavingsCart has retail savings
cart.hasCouponA discount code is applied
cart.total > 50Cart total greater than 50
cart.total >= 100Cart total ≥ 100
cart.shipping > 0Shipping cost > 0
cart.quantity >= 3Total quantity ≥ 3

Cart Formatting Options

Use on the same element as data-next-display.

AttributeDescriptionExample
data-formatOutput formatcurrency, number, integer, percentage
data-hide-zero-centsHide .00 for whole numbers"true"$99
data-hide-if-zeroHide element when value is 0"true"
data-hide-if-falseHide element when value is false"true"
data-divide-byDivide displayed value"2"
data-multiply-byMultiply displayed value"0.1"

Cart Items List

Use data-next-cart-items on a container to render cart line items from a template.

AttributeDescriptionExample
data-next-cart-itemsContainer where cart line items are renderedOn wrapper <div>
data-item-template-idID of <template> used for each item"cart-item-template"
data-item-template-selectorCSS selector for template node"#custom-template"
data-item-templateInline HTML template string'<div>{item.name}</div>'
data-empty-templateHTML when cart is empty'<p>Your cart is empty</p>'
data-title-mapJSON: package ID → custom title'{"2": "Premium", "3": "Starter"}'
data-group-itemsGroup identical items into one linePresent = on
data-include-discountsInclude discount codes in subtotalOn same element as data-next-display="cart.subtotal"

Item Template Variables

Used inside the template referenced by data-next-cart-items. Replace {item.xyz} with the value for each cart line.

Basic Item

VariableDescriptionExample
{item.id}Unique cart item IDAuto-generated
{item.packageId}Package/product ID"123"
{item.name}Product/package name"Grounded Sheets"
{item.title}Alias for nameSame as name
{item.quantity}Quantity in cart"2"
{item.image}Product image URL"https://..."
{item.sku}Product SKU/external ID"GRS-001"
{item.frequency}Purchase frequency"One time" / "Every month"

Product & Variant

VariableDescriptionExample
{item.productName}Base product name"Grounded Sheets"
{item.variantName}Full variant name"Grounded Sheets - Obsidian Grey / Twin"
{item.variantSku}Variant SKU"BE-GRS-TWN-GR"
{item.variantAttributesFormatted}Comma-separated attributes"Color: Obsidian Grey, Size: Twin"
{item.variantAttributesList}HTML spans<span class="variant-attr">...</span>
{item.variantColor}Color attribute"Obsidian Grey"
{item.variantSize}Size attribute"Twin"

Pricing

VariableDescriptionType
{item.price}Package priceCurrency
{item.unitPrice}Price per unitCurrency
{item.comparePrice}Original package priceCurrency
{item.unitComparePrice}Original unit priceCurrency
{item.lineTotal}price × quantityCurrency
{item.lineCompare}Original line totalCurrency
{item.recurringPrice}Recurring price (subscription)Currency

Savings

VariableDescriptionType
{item.savingsAmount}Total savings (retail)Currency
{item.unitSavings}Per-unit savingsCurrency
{item.savingsPct}Savings percentage"50%"

Coupon Discount

VariableDescriptionType
{item.discountAmount}Coupon discount for this itemCurrency
{item.discountedPrice}Price after couponCurrency
{item.discountedLineTotal}Line total after couponCurrency
{item.finalPrice}Final price to showCurrency
{item.finalLineTotal}Final line totalCurrency

Conditional Classes (use as class names)

VariableValueUse when
{item.showCompare}"show" / "hide"Compare price exists
{item.showSavings}"show" / "hide"Has savings
{item.showRecurring}"show" / "hide"Recurring product
{item.showDiscount}"show" / "hide"Coupon applied
{item.showOriginalPrice}"show" / "hide"Price is discounted

Item Action Attributes

Use on buttons inside an item template to control quantity or remove an item.

AttributeDescriptionExample
data-next-quantityChange quantity"increase" or "decrease"
data-next-remove-itemRemove this package from cartNo value
data-package-idPackage ID for the action"{item.packageId}"
data-cart-item-idCart line ID (in template)"{item.id}"

Full Checkout Example

A complete cart summary UI wiring up all cart and item attributes:

<div class="cart-summary" data-next-show="cart.hasItems">
  <h3>Cart Summary</h3>

  <!-- Cart item list with full template -->
  <template id="cart-item-full-template">
    <div class="cart-item" data-cart-item-id="{item.id}" data-package-id="{item.packageId}">
      <img src="{item.image}" alt="{item.name}">
      <h4>{item.name}</h4>
      <p>{item.productName} – {item.variantName}</p>
      <p>{item.variantAttributesFormatted}</p>
      <p class="price">{item.price} × {item.quantity} = {item.lineTotal}</p>
      <s class="{item.showCompare}">{item.comparePrice}</s>
      <span class="{item.showSavings}">Save {item.savingsAmount} ({item.savingsPct})</span>
      <span class="{item.showDiscount}">Coupon: -{item.discountAmount}</span>
      <span class="{item.showRecurring}">{item.recurringPrice}</span>
      <button data-next-quantity="decrease" data-package-id="{item.packageId}">−</button>
      <span>{item.quantity}</span>
      <button data-next-quantity="increase" data-package-id="{item.packageId}">+</button>
      <button data-next-remove-item data-package-id="{item.packageId}">Remove</button>
    </div>
  </template>

  <div data-next-cart-items
       data-item-template-id="cart-item-full-template"
       data-empty-template='<p class="empty-msg">Your cart is empty</p>'
       class="cart-items-list">
  </div>

  <!-- Cart totals -->
  <div class="cart-totals">
    <div><span>Subtotal:</span><span data-next-display="cart.subtotal">$0.00</span></div>
    <div data-next-show="cart.shipping > 0">
      <span>Shipping:</span>
      <span data-next-display="cart.shipping" data-hide-if-zero="true">$0.00</span>
    </div>
    <div data-next-show="cart.hasCoupon">
      <span>Discount (<span data-next-display="cart.discountCode">-</span>):</span>
      <span data-next-display="cart.discounts">-$0.00</span>
    </div>
    <div class="total">
      <span>Total (<span data-next-display="cart.currency">USD</span>):</span>
      <span data-next-display="cart.total" data-format="currency">$0.00</span>
    </div>
    <div data-next-show="cart.hasSavings">
      <span>You save:</span>
      <span data-next-display="cart.totalSavingsAmount">$0.00</span>
      (<span data-next-display="cart.totalSavingsPercentage" data-format="percentage">0%</span>)
    </div>
  </div>

  <!-- Free shipping threshold -->
  <div data-next-show="cart.total < 100">
    <p>Add $<span data-next-display="100 - cart.total.raw">0</span> more for free shipping!</p>
  </div>
  <div data-next-show="cart.total >= 100">
    <p>✓ Free shipping!</p>
  </div>

  <div data-next-hide="cart.isEmpty">
    <button type="button">Proceed to Checkout</button>
  </div>
</div>

<div class="cart-empty-state" data-next-show="cart.isEmpty">
  <p>Your cart is empty</p>
  <a href="/shop">Continue Shopping</a>
</div>

On this page