Shopify Cart API: AJAX Add-to-Cart in Themes
Shopify's Cart AJAX API lets theme developers add products without full page reloads. This guide covers /cart/add.js, /cart/change.js, cart drawer integration, and production-grade error handling.
Why the Cart AJAX API exists
Traditional form-based add-to-cart submits a POST to /cart/add and redirects the browser. That works, but it breaks the flow on modern storefronts where customers expect instant feedback: the cart icon updates, a drawer slides open, and the page never flickers. Shopify exposes a JSON-first Cart API through endpoints like /cart/add.js, /cart/change.js, /cart/update.js, and /cart/clear.js that return structured cart data instead of HTML.
Theme developers use these endpoints from JavaScript to build cart drawers, sticky add-to-cart bars, quick-buy modals, and bundle builders. The API is available on every Online Store 2.0 theme without OAuth or app credentials because it runs in the buyer's session context. That simplicity is powerful, but it also means you own every edge case: sold-out variants, quantity limits, selling plan mismatches, and network failures all surface in your UI code.
/cart/add.js: adding items programmatically
POST /cart/add.js accepts JSON or FormData. The most common payload is { items: [{ id: variantId, quantity: 1 }] }. The id field is the variant ID, not the product ID. You can pass properties for line-item customizations (engraving text, gift messages), selling_plan for subscriptions, and quantity up to inventory limits.
A minimal fetch implementation looks like this pattern: intercept the product form submit, preventDefault(), POST to /cart/add.js with Content-Type: application/json, then parse the response. On success Shopify returns the added line item object including key, variant_id, product_title, and image. On failure it returns { status: 422, message: '...', description: '...' } with human-readable error text you should display inline near the add button rather than as a generic alert.
For multiple variants at once, send several objects inside items. Shopify processes them atomically in a single request, which is ideal for 'Complete the look' bundles. Always debounce rapid clicks on add buttons; duplicate requests can briefly overshoot inventory before Shopify rejects the second call.
/cart/change.js and /cart/update.js: mutating the cart
/cart/change.js updates a single line item identified by its key (a unique string per line, not the variant ID). Payload: { id: lineItemKey, quantity: 2 }. Setting quantity to 0 removes the line. Use change when your cart drawer has per-row quantity steppers.
/cart/update.js accepts { updates: { [lineItemKey]: quantity } } to batch multiple line changes in one round trip, or { note: 'Gift wrap please' } to set cart attributes. After any mutation, Shopify returns the full cart object: item_count, total_price (in cents), items array, and cart-level attributes. Re-render your drawer from this response instead of patching local state manually; that avoids drift when Shopify applies automatic discounts or BOGO rules server-side.
For cart notes, attributes, and discount code fields, /cart/update.js is the correct endpoint. Discount codes themselves still go through /discount/{code} or checkout; do not assume update.js applies codes. Keep cart attribute names consistent (underscores, lowercase) so Liquid {% cart.attributes %} and your JS read the same keys.
Cart drawer integration patterns
A production cart drawer has three layers: data (cart state from the API), presentation (Liquid snippet or JS template), and orchestration (event bus or custom element). After /cart/add.js succeeds, fetch /cart.js (GET) for the complete cart including line-level discounts and compare-at prices, then inject HTML into the drawer container and call drawer.open().
Dispatch a custom event such as cart:updated with detail: { cart, source: 'add' } so unrelated components (header badge count, free-shipping progress bar, sticky ATC) stay in sync without tight coupling. ADSPOC themes wire this event through a single CartController module so predictive search, collection quick-add, and product page forms all share one code path.
Accessibility matters: move focus into the drawer on open, trap tab order, restore focus to the triggering button on close, and announce cart changes via an aria-live region ('2 items in cart, subtotal ₹1,499'). Screen reader users should never hear silence after an AJAX add.
Error handling and edge cases
HTTP 422 is the workhorse error for cart operations: variant sold out, quantity exceeds available inventory, product unpublished mid-session, or invalid selling plan. Parse response JSON and map message to UI. Never swallow 422 silently; that is the top cause of 'I clicked Add to Cart and nothing happened' support tickets.
Network timeouts and 5xx responses need retry UX with exponential backoff and a fallback link: 'Could not update cart. View cart or try again.' Session expiry (rare on Shopify) may return HTML login pages instead of JSON; detect non-JSON responses and reload the page.
Inventory reservations are not held at cart-add time on standard Shopify checkout. Two buyers can add the last unit; the second fails at checkout. Show real-time inventory only when using Shopify Markets inventory policies or an app; otherwise display 'Low stock' badges from Liquid and accept that AJAX add may still 422 at the edge.
Test with: single-variant and multi-variant products, max-quantity products, subscription variants, products with required line-item properties, and carts over the 500-line limit (Shopify enforces a maximum). Automated theme checks rarely cover AJAX cart flows; manual QA on mobile Safari is non-negotiable.
Frequently asked questions
Get a free conversion audit from India's best Shopify builders
ADSPOC since 2000 · India's #1 CRO-focused Shopify agency · any store type · 18-day delivery or money back · 23+ conversion features built in · WhatsApp direct line · trained thousands of developers · Mumbai & Solan, serving India, Bangladesh, Pakistan, and worldwide.
Prefer a quick chat? Message ADSPOC on WhatsApp
Related reading
Shopify Dawn Theme: A Developer's Anatomy Guide
Dawn is Shopify's reference Online Store 2.0 theme. Learn its file structure, section patterns, cart drawer implementation, predictive search, and what to copy into custom builds.
Storefront API vs Admin API: Which Should Developers Use?
Shopify exposes two GraphQL APIs with different trust boundaries. Learn when to use Storefront API for buyer-facing custom UI, Admin API for backend operations, and where Online Store themes fit.
Theme App Extensions and App Blocks Explained
Theme app extensions let Shopify apps embed Liquid blocks in merchant themes without editing theme code. Learn @app blocks, deep linking, deployment, and OS 2.0 integration patterns.