> For the complete documentation index, see [llms.txt](https://analog.groupthought.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://analog.groupthought.com/reference/alpine/cart-form.md).

# x-cart-form

Directives and a magic for cart forms: cart updates with fetch, with cart sections updated in place.

Directives and a magic for cart forms: cart updates with fetch, with cart sections updated in place.

| Directive / magic               | What it does                                                                                                                                              |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `x-cart-form`                   | The root, on the `{% form 'cart' %}` element or a parent of one                                                                                           |
| `x-cart-form.wait-error.3000ms` | Modifiers pass through to `x-async-form`: `wait-success` (default 1000ms) and `wait-error`                                                                |
| `x-cart-form:context`           | Holds the form state. The root binds it for you                                                                                                           |
| `x-cart-form:form`              | The form element. Sets `method="POST"`. The root binds it for you                                                                                         |
| `$cartForm`                     | The same API as [`$asyncForm`](/reference/alpine/async-form.md): `state`, `parsedBody`, `errorMessage`, `errorDescription`, `submitAsync()`, `reset()`, … |

Submission goes to `/cart/update.js` (`window.theme.routes.cartUpdate`). The plugin appends the ids of every section marked `data-cart-api-section` to the request's `sections` parameter. The response includes updated HTML for the Cart page, cart drawer, and header bubble, which re-render without a page reload.

On top of the [`x-async-form` events](/reference/alpine/async-form.md), the plugin dispatches the standard events before the request: `shopify:cart:note-update` when the form's `note` field changed from its server-rendered value, `shopify:cart:lines-update` for its `updates[...]` quantities. Lines set to 0 use action `'remove'`; the rest use `'update'`. A submission with both dispatches separate events. Failures dispatch `shopify:cart:error`. See [Events](/developer-platform/events.md).

Use [x-cart-item](/reference/alpine/cart-item.md) to change one line item's quantity. It submits per line and handles stockouts.

## Examples

### A basic cart form

Use `x-cart-form` on the cart form to save `updates[]` quantities without a reload:

```liquid
{% form 'cart', cart, x-cart-form: '' %}
  {% for item in cart.items %}
    <input type="number" name="updates[]" value="{{ item.quantity }}">
  {% endfor %}
  <button type="submit">Update cart</button>
{% endform %}
```

Without JavaScript the form submits normally to `/cart` and Shopify renders the cart page.

### Saving the cart note as the customer types

Use `requestSubmit()` from an input listener to submit without a button. The customer-notes block saves the note this way:

```liquid
<div x-cart-form>
  {% form 'cart', cart %}
    <textarea name="note" @input.debounce.500ms="$el.closest('form').requestSubmit()">
      {{- cart.note -}}
    </textarea>
  {% endform %}
</div>
```

`x-cart-form` sits on the wrapper, so the plugin finds the `<form>` inside and markup outside it can read `$cartForm`.

### Showing progress

Use `$cartForm.state` to show a spinner while the update runs and a check when it lands:

```html
<div class="relative size-icon">
  <span x-show="$cartForm.state === 'pending'" x-transition.opacity>
    {% render 'spinner' %}
  </span>
  <span x-show="$cartForm.state === 'success'" x-transition.opacity>
    {% render 'core-icon', icon: 'stroke-bare-check' %}
  </span>
</div>
```

The success state returns to idle after the `wait-success` timeout (default 1000ms), so the check fades out on its own.

## Customizing

The Cart page and drawer use this plugin for notes, shipping estimates, and review blocks. See [Cart](/features/cart.md).
