DocsAlpineWorking with the cart
Working with the cart
Drive Shopify's cart from a CascadeKit section using Alpine and the AJAX cart API.
The cart API
Shopify exposes the cart over a small set of JSON endpoints. CascadeKit's cart sections wrap these with Alpine so the UI updates without a page reload.
/cart.jsRead the current cart as JSON.
/cart/add.jsAdd a variant by id and quantity.
/cart/change.jsChange the quantity of a line item.
/cart/clear.jsEmpty the cart.
Add to cart
Post the variant id to /cart/add.js and reflect the result in Alpine state:
snippets/add-to-cart.liquid
<div x-data="{ loading: false }"><button x-on:click=" loading = true; await fetch('/cart/add.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: {{ product.selected_variant.id }}, quantity: 1 }) }); loading = false; $dispatch('cart:refresh'); " :disabled="loading" class="rounded-control bg-neutral-900 px-5 py-2.5 text-white"> <span x-text="loading ? 'Adding…' : 'Add to cart'"></span></button></div>Refresh the drawer
The $dispatch('cart:refresh') above lets the cart drawer listen and re-fetch itself — components stay decoupled:
sections/cart-drawer.liquid
<divx-data="{ items: [] }"x-on:cart:refresh.window=" const cart = await (await fetch('/cart.js')).json(); items = cart.items;"><template x-for="item in items" :key="item.key"> <p x-text="item.product_title"></p></template></div>Always re-read the cart
Don't trust optimistic counts. After any mutation, re-fetch /cart.js so totals and line items stay in sync with Shopify.