Next Commerce
CampaignsGuides

Cart Summary

The Cart Summary displays the cart totals — subtotal, discounts, shipping, and grand total — and updates automatically whenever the cart changes. Drop it anywhere on the page: a sidebar, a sticky footer, or an order review step.

What You're Building

  • A live price breakdown that re-renders when items are added, removed, or discounts change
  • Optional custom layout with your own markup
  • Conditional rows that appear only when relevant (e.g. a discount row only when a coupon is active)
  • An itemised line list with per-item pricing

Step 1: Minimal Setup

One attribute is all you need. The enhancer renders a built-in Subtotal / Discounts / Shipping / Total layout:

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

The built-in template only shows the discount row when discounts are greater than zero. No extra configuration needed.

Step 2: Custom Template

Place a <template> inside the container to replace the default layout with your own markup. Use {variable} placeholders for dynamic values.

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

Available Template Variables

VariableDescription
{subtotal}Subtotal before shipping and discounts
{total}Grand total
{shipping}Shipping cost (formatted, or "Free" when zero)
{shippingOriginal}Original shipping before any shipping discount (empty if none)
{shippingDiscountAmount}Absolute amount saved on shipping
{shippingDiscountPercentage}Shipping discount as a percentage (e.g. "10%")
{shippingName}Display name of the selected shipping method
{shippingCode}Code of the selected shipping method
{totalDiscount}Total discount amount (offers + vouchers combined)
{totalDiscountPercentage}Total discount as a percentage of subtotal (e.g. "20%")
{discounts}Deprecated alias for {totalDiscount} — still rendered
{currency}Active currency code (e.g. "USD")
{itemCount}Number of distinct lines in the cart
{totalQuantity}Total unit quantity across all lines
{isEmpty}"true" or "false"
{hasDiscounts}"true" or "false"
{isFreeShipping}"true" or "false"
{hasShippingDiscount}"true" or "false"
{isCalculating}"true" or "false"

Step 3: Conditional Rows with CSS Classes

The enhancer applies state classes to the container element. Use them to show or hide rows with CSS instead of writing JavaScript.

<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="row row-discounts"><span>Discounts</span><span>−{totalDiscount}</span></div>
    <div class="row row-shipping-discount">
      <span>Shipping</span>
      <del>{shippingOriginal}</del>
      <span>{shipping}</span>
    </div>
    <div class="row row-shipping-free"><span>Shipping</span><span>Free</span></div>
    <div class="row row-total"><span>Total</span><span>{total}</span></div>
  </template>
</div>

<style>
  /* Hide these rows by default */
  .row-discounts,
  .row-shipping-discount { display: none }

  /* Show when state classes are present */
  .next-has-discounts .row-discounts    { display: flex }
  .next-has-shipping-discount .row-shipping-discount { display: flex }
  .next-has-shipping .row-shipping-free { display: none }
  .next-free-shipping .row-shipping-free { display: flex }
</style>

State Classes Reference

ClassWhen applied
next-cart-emptyCart has no items
next-cart-has-itemsCart has one or more items
next-has-discountsDiscount amount > 0
next-no-discountsDiscount amount = 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-calculatingTotals recalculation is in progress
next-not-calculatingTotals are up to date

Step 4: Itemised Line List

Show a breakdown of each cart line — useful for an order review section. Add a [data-summary-lines] container inside your template with a <template> child for the row markup.

<div data-next-cart-summary>
  <template>
    <ul class="line-items" data-summary-lines>
      <template>
        <li class="line-item">
          <img src="{item.image}" alt="{item.name}" />
          <div class="line-info">
            <strong>{item.name}</strong>
            <span>{item.variantName}</span>
          </div>
          <div class="line-pricing">
            <span>{item.quantity} × {item.unitPrice}</span>
            <strong>{item.price}</strong>
          </div>
        </li>
      </template>
    </ul>

    <div class="summary-row"><span>Subtotal</span><span>{subtotal}</span></div>
    <div class="summary-row"><span>Shipping</span><span>{shipping}</span></div>
    <div class="summary-row total-row"><span>Total</span><span>{total}</span></div>
  </template>
</div>

Line Item Variables

Use the {item.*} token namespace inside data-summary-lines row templates.

VariableDescription
{item.packageId}Package ref_id
{item.name}Package display name
{item.image}Product image URL
{item.productName}Product name
{item.variantName}Variant name (e.g. "Black / Large")
{item.sku}Product SKU
{item.quantity}Quantity in cart
{item.unitPrice}Unit price after discounts
{item.originalUnitPrice}Unit price before discounts
{item.price}Package price after discounts
{item.originalPrice}Package price before discounts
{item.discountAmount}Total discount on this line
{item.discountPercentage}Discount as a percentage of original unit price
{item.hasDiscount}"show" or "hide" — useful as a CSS class
{item.isRecurring}"true" or "false"
{item.interval}Billing interval ("day", "month", or empty)
{item.intervalCount}Number of intervals per cycle
{item.frequency}Human-readable frequency (e.g. "Monthly")
{item.recurringPrice}Recurring unit price (subscriptions)
{item.currency}Currency code for this line

The legacy {line.*} token set is deprecated — those tokens render as empty strings and emit a console warning. Always use {item.*} in new templates.

Step 5: Discount Breakdowns

Show applied offer discounts and coupon codes as separate line items. Add list containers inside your template:

<div data-next-cart-summary>
  <template>
    <div class="row"><span>Subtotal</span><span>{subtotal}</span></div>

    <!-- Offer discounts (e.g. "Buy 2 get 10% off") -->
    <ul data-summary-offer-discounts>
      <template>
        <li class="discount-item">
          <span>{discount.name}</span>
          <span>−{discount.amount}</span>
        </li>
      </template>
    </ul>

    <!-- Applied coupon codes -->
    <ul data-summary-voucher-discounts>
      <template>
        <li class="discount-item coupon">
          <span>{discount.name}</span>
          <span>−{discount.amount}</span>
        </li>
      </template>
    </ul>

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

Discount item variables: {discount.name}, {discount.amount}, {discount.description}.

Full Example

A complete order review summary with line items, discount list, and conditional shipping row:

<div class="order-summary" data-next-cart-summary>
  <template>
    <h3>Order Summary</h3>
    <p class="item-count">{itemCount} item(s)</p>

    <ul class="line-items" data-summary-lines>
      <template>
        <li class="line-item">
          <img src="{item.image}" alt="{item.name}" />
          <div class="line-info">
            <strong>{item.name}</strong>
            <span class="variant">{item.variantName}</span>
          </div>
          <div class="line-price">
            <span>{item.quantity} × {item.unitPrice}</span>
            <strong>{item.price}</strong>
          </div>
        </li>
      </template>
    </ul>

    <div class="divider"></div>

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

    <ul class="discounts" data-summary-offer-discounts>
      <template>
        <li><span class="discount-name">{discount.name}</span><span>−{discount.amount}</span></li>
      </template>
    </ul>

    <ul class="discounts" data-summary-voucher-discounts>
      <template>
        <li><span class="coupon-name">{discount.name}</span><span>−{discount.amount}</span></li>
      </template>
    </ul>

    <div class="summary-row shipping-row">
      <span>Shipping</span>
      <span>{shipping}</span>
    </div>

    <div class="divider"></div>

    <div class="summary-row discount-row">
      <span>You save ({totalDiscountPercentage})</span>
      <span class="discount-amount">−{totalDiscount}</span>
    </div>

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

<style>
  .order-summary {
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 10px;
    padding: 1.5rem;
  }
  .line-items { list-style: none; padding: 0; margin: 0 0 1rem }
  .line-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0;
  }
  .line-item img { width: 48px; height: 48px; object-fit: cover; border-radius: 4px }
  .line-info { flex: 1 }
  .variant { color: #6b7280; font-size: 0.85rem }
  .summary-row {
    display: flex;
    justify-content: space-between;
    padding: 0.35rem 0;
  }
  .total-row { font-size: 1.1rem; font-weight: 700 }
  .divider { border-top: 1px solid #e5e7eb; margin: 0.75rem 0 }
  .discounts { list-style: none; padding: 0; color: #16a34a }
  .discounts li { display: flex; justify-content: space-between }

  /* Hide discount row when no discount is applied */
  .next-no-discounts .discount-row { display: none }
  .discount-amount { color: #16a34a }
</style>

On this page