# Cart Page

> Build a cart page with editable line items, empty state, coupons, shipping methods, totals, and notices.

# Cart Page

A cart page lets customers review products, change quantities, remove items, apply coupons, choose shipping, and move to checkout.

## Minimum Structure

```text
Cart page
  CartNotices
  CartItems
    CartItemImage
    CartItemTitle
    CartAttributeList
      CartAttributeName
      CartAttributeValue
    CartItemPrice
    CartItemQuantity
    CartItemSubtotal
    CartItemRemove
    Empty slot
  CouponForm
  CouponAppliedList
    CouponRemoveButton
  ShippingMethodSelector
  CartTotalsList
    CartAmount (items subtotal)
    CartAmount (discount)
    CartAmount (fees)
    CartAmount (shipping)
    CartAmount (tax)
    CartAmount (final total)
  Checkout link or button
```

<CodeExample title="Editable cart rows with totals" language="jsx">
{`<OmeWooCartNotices />

<OmeWooCartItems>
  {#slot default}
    <OmeWooCartItemImage />
    <OmeWooCartItemTitle />
    <OmeWooCartAttributeList>
      {#slot default}
        <OmeWooCartAttributeName />
        <OmeWooCartAttributeValue />
      {/slot}
    </OmeWooCartAttributeList>
    <OmeWooCartItemQuantity />
    <OmeWooCartItemSubtotal />
    <OmeWooCartItemRemove />
  {/slot}
  {#slot empty}
    Your cart is empty.
  {/slot}
</OmeWooCartItems>

<OmeWooCouponForm />
<OmeWooCouponAppliedList>
  {#slot default}
    <OmeWooCouponRemoveButton />
  {/slot}
</OmeWooCouponAppliedList>

<OmeWooShippingMethodSelector ui='{{"mode":"button-radio","aria_label":"Choose shipping"}}' />

<OmeWooCartTotalsList>
  <OmeWooCartAmount
    amount='{{"measure":"items_subtotal","tax_presentation":"store"}}'
    content='{{"show_label":true,"label":""}}'
  />
  <OmeWooCartAmount
    amount='{{"measure":"discount","tax_presentation":"store"}}'
    content='{{"show_label":true,"label":""}}'
  />
  <OmeWooCartAmount
    amount='{{"measure":"fees","tax_presentation":"store"}}'
    content='{{"show_label":true,"label":""}}'
  />
  <OmeWooCartAmount
    amount='{{"measure":"shipping","tax_presentation":"store","pending_text":"Not calculated"}}'
    content='{{"show_label":true,"label":""}}'
  />
  <OmeWooCartAmount
    amount='{{"measure":"tax"}}'
    content='{{"show_label":true,"label":""}}'
  />
  <OmeWooCartAmount
    amount='{{"measure":"final_total"}}'
    content='{{"show_label":true,"label":""}}'
  />
</OmeWooCartTotalsList>`}
</CodeExample>

## Step 1: Add Cart Rows

`CartItems` renders the current Store API cart items into the default slot. Cart item atom components do not query global cart state by themselves; they read the row context that `CartItems` hydrates.

This is why item atoms should be authored inside the `CartItems` default slot:

| Atom | Row value |
| --- | --- |
| `CartItemImage` | Current item image and alt fallback. |
| `CartItemTitle` | Current item title, optionally with attributes. |
| `CartAttributeList` | Current item variation/attribute list. |
| `CartItemPrice` | Current item unit price text. |
| `CartItemQuantity` | Current item quantity input and update action. |
| `CartItemSubtotal` | Current item subtotal text. |
| `CartItemRemove` | Current item remove action. |

If the cart becomes empty, `CartItems` swaps to the `empty` slot.

## Step 2: Let Row Actions Refresh The Cart

`CartItemQuantity` and `CartItemRemove` both send Store API cart mutation requests through the cart store. After a successful mutation, the normalized cart response updates:

- cart item rows,
- `CartCount`,
- coupon list,
- shipping rates,
- cart total atoms,
- notices.

Quantity edits are local drafts until Woo confirms them. Rapid same-row edits collapse to the newest absolute quantity, while different cart operations retain FIFO order. The quantity input stays editable during its short debounce; the shared count and totals do not change optimistically. Debounced-away and already-satisfied targets emit no events. Every real authoritative response emits `ome-woo:item-quantity-updated` followed by `ome-woo:cart-updated`, but the control shows and announces success only when the latest target is confirmed.

Set `configuration.counter_presentation="stepper"` on `CartItemQuantity` when the design needs decrease and increase buttons around a read-only count. The default remains the released number input. Both presentations use the same exact-line setter and Woo-provided minimum, maximum, multiple, editability, and sold-individually constraints; neither turns zero into removal.

Style local progress with `data-ome-woo-mutation-phase="debouncing|queued|pending|success|error|idle"`. Existing `data-ome-state="loading|success|error|idle"` selectors continue to work.

## Step 3: Add Coupons

`CouponForm` applies a coupon code. `CouponAppliedList` renders the current applied coupons, and `CouponRemoveButton` belongs inside each coupon row. The cart demo keeps `CartNotices` near the top of the page so cart errors share one cart-level feedback region. If a page omits Cart Notices, the runtime announces the final safe error through one visually hidden assertive fallback; it does not create duplicate feedback when Cart Notices is authored. Final confirmed quantity and removal results use one polite announcement; intermediate targets and internal scheduling phases are not narrated.

## Step 4: Add Shipping And Totals

`ShippingMethodSelector` uses the same UIChoice-backed modes as checkout selectors. Use it when the cart needs a selectable shipping rate before checkout. The selected Store API shipping rate refreshes total values.

`CartTotalsList` renders authored total rows. For new work, add one `CartAmount` per shopper measure and reorder or wrap those rows as needed. `tax_presentation="store"` follows the WooCommerce cart tax-display setting for subtotal, discount, fees, and shipping; explicit `including_tax` and `excluding_tax` presentations are also available for those four measures.

Shipping has two intentionally different zero-like cases. Calculated free shipping renders the formatted currency zero. Required shipping that has not been calculated renders `Not calculated` with `data-ome-state="pending"`; the direct final total remains visible with `data-ome-state="estimated"` until shipping resolves.

`TotalsSubtotal`, `TotalsDiscount`, `TotalsShipping`, `TotalsTax`, and `TotalsTotal` remain available as unchanged legacy atoms for existing designs. Do not silently replace saved atoms: their flat raw fields and compatibility formatting are not equivalent to Cart Amount's shopper-facing tax and pending-state behavior.

## Step 5: Link To Checkout

The checkout URL should point to the page assigned in WooCommerce as `woocommerce_checkout_page_id`. Read [Woo Template Integration Contract](/woo/flows/template-integration-contract) before building a starter that owns cart and checkout routes.

## Next References

- [Cart Items](/woo/component-families/cart-items) explains cart row atoms.
- [Coupons](/woo/component-families/coupons) explains coupon apply and remove components.
- [Shipping Selector](/woo/component-families/shipping-selector) explains shipping modes and package-aware values.
- [Cart Totals](/woo/component-families/cart-totals) explains total atoms.
