> ## Documentation Index
> Fetch the complete documentation index at: https://docs.elasticfunnels.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Cart

> Add a cart to your page so visitors see their items, quantities, and total—and can change quantity or go to checkout

The cart shows what the visitor has added to their order: each item, quantity, price, and the total. It updates live when they add items or change quantities, without reloading the page.

## Adding a cart to your page

In the page builder you can add:

* **Cart** – A single cart block (list of items + total + checkout).
* **Cart Dropdown** – A "Cart" trigger (e.g. with item count) that opens a dropdown panel with the same cart inside. Handy in headers or sidebars.

Both use the same cart data; only the placement and layout differ.

### Where to find them

1. Open the **Elements** or **Blocks** panel.
2. In the **Cart** category, choose **Cart** or **Cart Dropdown**.
3. Drag the block onto your page where you want it (e.g. below the fold for Cart, in the top bar for Cart Dropdown).

The default Cart block already includes:

* A row per item (name, quantity controls, unit price, line total).
* A footer with **Total** and a **Checkout** link.

You can keep this layout or edit it (e.g. change labels, order of fields, or styling).

## What visitors see

* **Item name** – From your product/offer (e.g. "1 Bottle").
* **Item image** – Product thumbnail (if the product has one).
* **Quantity** – Current quantity with **−** and **+** to decrease or increase.
* **Unit price** – Price per unit (e.g. "\$69.00").
* **Line total** – Price × quantity for that item (e.g. "\$552.00").
* **Cart total** – Sum of all line totals, shown in the footer (e.g. "Total: \$552.00").
* **Checkout** – Link to continue to checkout.

All of this updates automatically when they add to cart or change quantity.

## Customizing the cart layout

The cart is built from a **template**: you can change text, order of fields, and styling in the builder.

### Cart-level variables

These apply to the cart as a whole:

| Variable                          | Description                                                                                 |
| --------------------------------- | ------------------------------------------------------------------------------------------- |
| **`[[ efCart.total ]]`**          | Grand total of all items (formatted, e.g. "\$138.00")                                       |
| **`[[ efCart.subtotal ]]`**       | Same as `efCart.total` (alias)                                                              |
| **`[[ efCart.count ]]`**          | Total quantity across all items (sum of quantities, e.g. "2") — use for an item count badge |
| **`[[ efCart.item_count ]]`**     | Number of distinct product lines in the cart                                                |
| **`[[ efCart.discount_total ]]`** | Total savings across all items (formatted)                                                  |
| **`[[ efCart.currency ]]`**       | Currency code (e.g. "USD")                                                                  |

### Per-item row variables

Inside a `template-foreach` loop, each item exposes:

| Variable                         | Description                                                                                               |
| -------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **`[[ item.name ]]`**            | Item name                                                                                                 |
| **`[[ item.image ]]`**           | Product image URL                                                                                         |
| **`[[ item.code ]]`**            | Product code (needed for quantity controls)                                                               |
| **`[[ item.quantity ]]`**        | Current quantity                                                                                          |
| **`[[ item.price ]]`**           | Unit price (formatted, e.g. "\$69.00") — do not apply `formatPrice`                                       |
| **`[[ item.total ]]`**           | Line total (formatted, e.g. "\$138.00") — do not apply `formatPrice`                                      |
| **`[[ item.retail_price ]]`**    | Original/retail price (formatted) — shown when there is a discount                                        |
| **`[[ item.discount_total ]]`**  | Per-line savings (formatted)                                                                              |
| **`[[ item.currency ]]`**        | Currency code for this item                                                                               |
| **`[[ item.is_subscription ]]`** | `true` when this is a subscription product                                                                |
| **`[[ item.subscription ]]`**    | Subscription billing details — `null` for one-time products (see [Subscription info](#subscription-info)) |
| **`[[ item.bonuses ]]`**         | Free bonus products linked to this item (see [Bonus products](#bonus-products))                           |
| **`[[ item.courses ]]`**         | Courses linked to this product (see [Courses](#courses))                                                  |

<Info>
  **`item.price`** and **`item.total`** are already formatted. Use them as-is. See [Frontend Template Engine — Cart](/frontend-template-engine/cart) for the full data attributes reference.

  `bonuses`, `courses`, `is_subscription`, and `subscription` are populated after a brief async fetch when the cart loads. They are always present so templates never error on an undefined value.
</Info>

### Subscription info

When `item.is_subscription` is `true`, `item.subscription` contains:

| Property                | Description                                     |
| ----------------------- | ----------------------------------------------- |
| **`frequency`**         | Billing interval number (e.g. `3`)              |
| **`frequency_unit`**    | Unit: `"day"`, `"week"`, `"month"`, or `"year"` |
| **`trial_days`**        | Free trial days (`0` = no trial)                |
| **`first_charge_free`** | `true` when the first cycle is free             |

```html theme={null}
<template-if data-condition="item.is_subscription">
  <span>Every [[ item.subscription.frequency ]] [[ item.subscription.frequency_unit ]]</span>
</template-if>
```

The same fields are available on the checkout scope for single-product checkout pages:
`[[ checkout.is_subscription ]]` and `[[ checkout.subscription.frequency ]]` etc.

### Bonus products

Products can have **bonus items** attached — free gifts or complementary products included with the purchase (configured in the product catalog). They are available as `item.bonuses` — always an array, empty when no bonuses are configured.

Each bonus has: `code`, `name`, `image`, `price`, `retail_price`, `original_price`, `currency`.

```html theme={null}
<template-foreach data-each="item in cartItems">
  <div class="cart-line">
    <img src="[[ item.image ]]" alt="[[ item.name ]]" />
    <span>[[ item.name ]]</span>
    <span>[[ item.price ]]</span>
    <template-foreach data-each="bonus in item.bonuses">
      <div class="cart-bonus-row">
        <img src="[[ bonus.image ]]" alt="[[ bonus.name ]]" />
        <span>[[ bonus.name ]]</span>
        <span>FREE <s>[[ bonus.retail_price ]]</s></span>
      </div>
    </template-foreach>
  </div>
</template-foreach>
```

### Courses

When a product has linked courses (e.g. digital bonuses delivered via the course platform), they are available as `item.courses` — always an array, empty when no courses are linked.

Each course has: `id`, `title`, `slug`, `description`, `cover`, `instructor_name`, `status`.

```html theme={null}
<template-foreach data-each="course in item.courses">
  <div class="cart-course-row">
    <img src="[[ course.cover ]]" alt="[[ course.title ]]" />
    <span>[[ course.title ]]</span>
  </div>
</template-foreach>
```

Both the `[[ ]]` placeholder syntax and the **`ef-text`** binding attribute are supported. They are equivalent — use whichever fits your template:

```html theme={null}
<!-- placeholder syntax -->
<span>[[ efCart.total ]]</span>
<span>[[ item.price ]]</span>

<!-- ef-text binding (fallback content shown in builder, replaced at runtime) -->
<span ef-text="efCart.total">$0.00</span>
<span ef-text="item.price">$0.00</span>
```

If you add or reorder these, the cart will still update correctly as long as you keep the placeholders or bindings in place. For more on this syntax, see [Frontend Template Engine — Syntax](/frontend-template-engine/syntax).

## Quantity controls

The cart plugin handles **−**, **+**, and **Remove** automatically via data attributes. No custom script is needed.

| Attribute                         | Element                 | Purpose                                                                                      |
| --------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------- |
| **`data-cart-qty-minus`**         | Button                  | Decrease quantity by 1                                                                       |
| **`data-cart-qty-plus`**          | Button                  | Increase quantity by 1                                                                       |
| **`data-cart-qty-remove`**        | Button/link             | Remove line (sets qty to 0)                                                                  |
| **`data-code="[[ item.code ]]"`** | Same element            | Links the button to the product                                                              |
| **`data-disable-when-one`**       | Minus button (optional) | Disables the button when `item.quantity` is 1 (prevents accidentally removing the last unit) |

On checkout pages the minus button is always disabled at quantity 1, regardless of `data-disable-when-one`.

## Cart dropdown

The **Cart Dropdown** block gives you:

* A **trigger** (e.g. "Cart" plus the number of items) that visitors click to open the cart.
* A **panel** that opens below (or in a suitable position) with the full cart inside: same items, quantities, totals, and checkout link.

Show the item count in the trigger with **`[[ efCart.count ]]`**.

Use it when you want the cart in a fixed spot (e.g. header) without taking space until the visitor opens it.

## Checkout link

The cart plugin automatically resolves the checkout URL from your merchant settings and wires it to any element with the class **`.ef-cart-checkout`** or the attribute **`data-ef-cart-checkout`**. You do not need to hard-code the checkout URL in the builder.

The resolved URL is built as follows (in priority order):

1. The element's own `href` attribute (if it is a real path, not `#` or `#checkout`).
2. A `data-ef-cart-checkout-href` attribute on the element.
3. The checkout page configured in your merchant settings (injected as `window.efScope.checkout_url`).
4. Falls back to `/checkout` if nothing else resolves.

Current-page query parameters are automatically forwarded to the checkout URL (tracking/UTM parameters, etc.), with internal routing keys stripped.

<Info>
  The default Checkout button added by the builder already has `.ef-cart-checkout`. You only need to set a custom `href` if you want to override the merchant-configured checkout page.
</Info>

## Cart storage

The cart is stored in a **browser cookie** (`ef_cart_{brandId}`), not localStorage. Cookies persist for **365 days**, so the cart survives navigation and browser restarts within the same browser.

The cart is shared across all pages on the same domain, so the Cart Dropdown in the header and a full Cart block below the fold always show the same contents.

## JavaScript API

The cart plugin exposes a `window.ef` object you can call from custom scripts or `@click` handlers in your templates:

| Method                                               | Description                                                                                                                                  |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **`window.ef.addToCart(code, options?, quantity?)`** | Add a product to the cart. If already in the cart, quantity increases. `options` can include `{ price, name, currency }`.                    |
| **`window.ef.setCartQuantity(code, quantity)`**      | Set an absolute quantity. Pass `0` to remove the product.                                                                                    |
| **`window.ef.removeFromCart(code, allowClear?)`**    | Decrease quantity by 1. When `allowClear` is `false` (default), does nothing if quantity is already 1 (prevents going to 0).                 |
| **`window.ef.getCart()`**                            | Returns the raw cart array `[{ code, quantity }, ...]` (for debugging or custom UI).                                                         |
| **`window.ef.getCartForSubmit()`**                   | Same as `getCart()` but excludes zero-quantity lines.                                                                                        |
| **`window.ef.onAddToCart(callback)`**                | Run a callback when a product is added (client cart or `/b` buy links). Returns an unsubscribe function. See [EF API](/funnels/ef-api#cart). |

Example — add to cart on button click:

```html theme={null}
<button @click="window.ef.addToCart('BOTTLE-1')">Add to Cart</button>
```

Example — set quantity from an input:

```html theme={null}
<input type="number" @change="window.ef.setCartQuantity('BOTTLE-1', parseInt($event.target.value))" />
```

## `cart-updated` event

Every time the cart changes (add, remove, set quantity), the plugin dispatches a **`cart-updated`** custom event on `window`. You can listen to it to react to cart mutations in custom scripts:

```js theme={null}
window.addEventListener('cart-updated', (e) => {
  console.log('Cart changed:', e.detail.cart);
});
```

The event `detail` contains `{ brandId, cart }` where `cart` is the updated array.

<Tip>
  `cart-updated` fires on **any** cart change (add, remove, quantity). If you only care about items being **added** — and want the `/b` buy links covered too — use [`window.ef.onAddToCart(callback)`](/funnels/ef-api#cart) instead.
</Tip>

## Add-to-cart buttons

Buttons that add products to the cart use `data-ef-add-to-cart` (or class `.ef-add-to-cart`) together with `data-product-code`. These attributes also let the plugin read the price without an extra API request:

| Attribute                          | Purpose                                   |
| ---------------------------------- | ----------------------------------------- |
| **`data-product-code`**            | Product code to add                       |
| **`data-ef-product-price`**        | Unit price (numeric, e.g. `69.00`)        |
| **`data-ef-product-name`**         | Display name                              |
| **`data-ef-product-image`**        | Image URL                                 |
| **`data-ef-product-currency`**     | Currency code (default `USD`)             |
| **`data-ef-product-retail-price`** | Original/retail price for savings display |

When these attributes are present, adding to cart stores the price locally, so `item.price` and `item.total` are available immediately without waiting for the `/api/cart-products` response.

## Tips

* **Placement** – Put the cart or cart dropdown where visitors expect it (e.g. top-right for dropdown, above the fold for a full cart block).
* **Sticky / mobile** – You can place the cart in a sticky bar or in a mobile menu; the same block works anywhere.
* **Multiple carts** – You can add more than one cart or dropdown on a page; they all show the same cart data and stay in sync when the visitor changes quantity or adds items.
* **Item count badge** – Use `[[ efCart.count ]]` anywhere on the page (e.g. in the header next to "Cart") to show the total number of items in the cart.
* **Savings display** – Show `[[ efCart.discount_total ]]` in the cart footer or `[[ item.retail_price ]]` beside the unit price to highlight discounts when a retail price is set.
* **Subscription label** – Use `[[ item.is_subscription ]]` with `template-if` to show billing cadence (e.g. "Every 3 months") only for subscription products.
* **Bonuses & courses** – Use nested `template-foreach` on `item.bonuses` and `item.courses` to list included free products or digital courses beneath each cart line.
