Hello! Welcome back to our hands-on exploration of Shopify's JavaScript APIs.
In our last lesson, you successfully implemented an asynchronous "add to cart" feature. You learned to intercept a form submission, send its data to the /cart/add.js endpoint using the Fetch API, and then update the UI with the new cart state. This was a crucial first step in creating a modern, fluid shopping experience.
Today, we'll build directly on that foundation. A user who has added items to their cart needs to be able to manage them. Our learning outcome is to update cart quantities and remove items from a cart drawer using the Ajax API. We'll focus on implementing the logic for the ubiquitous "+" and "-" buttons next to a line item, and the "remove" functionality.
You're already familiar with making asynchronous requests and handling responses from your full-stack background. Now, we'll apply those skills to cart modification and tackle a common challenge in dynamic web applications: keeping the UI in sync with the server state.
1. The Core Challenge: Keeping a Dynamic Cart Updated
When we use the Ajax API to change the cart (e.g., update a quantity from 2 to 3), the change happens on Shopify's servers. However, the HTML for the cart drawer or mini-cart visible to the user is now out of date. It still shows a quantity of 2.
We need a strategy to refresh our cart's HTML. There are two main approaches:
-
Manual DOM Manipulation: We could parse the JSON response from the API and use JavaScript to find and update every single element: the line item's quantity, its subtotal, the cart's overall subtotal, the cart's item count, etc. This is complex and brittle. If you change the Liquid template later, you must also update the JavaScript, and it's easy to miss something.
-
Re-rendering with the Section Rendering API: Shopify provides a powerful and elegant solution. We can ask Shopify to re-render just our cart drawer
sectionwith the updated cart data and send us the fresh HTML. We then simply replace the old HTML in our drawer with the new HTML.
For components like a cart drawer, the second approach is almost always superior. It's more robust, easier to maintain, and leverages Shopify's own rendering engine. It keeps your Liquid template as the single source of truth for the structure of your cart.
Let's see this pattern in action.
Creating a Shopify ajax cart drawer
The video 'Creating a Shopify ajax cart drawer' by Coding with Robby demonstrates this exact pattern. We'll use this video as our practical guide for today's lesson.
Watch from 17:52 to 21:30. Pay close attention to the updateCartDrawer function. Notice how it uses fetch with a special URL (/?section_id=cart-drawer) to get the rendered HTML of just that section, and then uses innerHTML to replace the content on the page.
This /?section_id= endpoint is a key technique in modern Shopify theme development, and we will use it as the final step in our update process.
2. The API Endpoints for Cart Modification
Before we write the code, let's consult the official documentation to understand our tools. The Ajax API offers two primary endpoints for changing line items.
The official Cart API reference is the definitive source for understanding how these endpoints work. We'll look at two specific sections.
First, read the subsection titled 'Update line item quantities' under POST /{locale}/cart/update.js. Note how it uses an updates object and recommends using the line item key. Then, review the section for POST /{locale}/cart/change.js to see a slightly different approach for changing a single item.
From the documentation, we learn:
POST /cart/update.js: This is a powerful endpoint that lets you update multiple line items in a single request. You send anupdatesobject where keys are theline_item.keyand values are the new quantities.POST /cart/change.js: This endpoint is designed to change a single line item. You identify the item using itsid(variant ID or key) orline(its 1-based index in the cart) and provide the new quantity.
Crucially, for both endpoints, you can remove an item by setting its quantity to 0.
While change.js is fine, update.js is often more versatile. We will focus our implementation on update.js, following the example in our main video resource.
3. Implementing Quantity Updates
Let's build the functionality for the "+" and "-" buttons in our cart drawer.

The process involves four steps: gathering the required data, attaching event listeners, making the API call, and refreshing the UI.
Step 1: Pass the Line Item Key from Liquid to JavaScript
To tell Shopify which item to update, we need its unique key. The best way to make this available to our JavaScript is to add it as a data- attribute in our Liquid template for the cart drawer.
As shown in the "Coding with Robby" video (at 24:35), you would find the element that wraps each line item in your cart drawer's Liquid file and add the attribute:
{% comment %} Inside your cart drawer's for-loop over cart.items {% endcomment %}
<div class="cart-item" data-line-item-key="{{ item.key }}">
...
<input type="number" value="{{ item.quantity }}">
...
</div>
Now, when a user clicks a button within this div, we can use JavaScript to find this parent element and read its data-line-item-key.
Step 2: Handle Events and State
When a user clicks a "+" or "-" button:
- We need to attach a
clickevent listener to these buttons. - Inside the listener, we'll find the closest parent line item element to get the
key. - We'll find the quantity input to get the current quantity.
- We'll calculate the new quantity (current + 1 or current - 1).
A significant challenge arises here. Because we replace the entire cart drawer's HTML after each update, any event listeners we attached to the original buttons are destroyed.
The solution is to wrap our event listener attachments in a function. We'll call this function once on page load, and then call it again every time we successfully re-render the cart drawer.
The "Coding with Robby" video addresses this exact problem between 26:15 and 28:10, creating a function named addCartDrawerListeners to solve it. This is a critical pattern for dynamic applications.
Step 3 & 4: Make the API Call and Update the UI
Now we have the lineItemKey and the newQuantity. We can make our fetch request.
Creating a Shopify ajax cart drawer
Let's watch the implementation of the quantity update logic. This segment combines the data gathering, API call, and UI update.
Watch from 23:03 to 36:18. This is a detailed section, so focus on these key moments: 23:03 - 25:10: Attaching event listeners and planning the logic. 28:50 - 33:50: Getting the line_item_key and newQuantity, then constructing the fetch request to /cart/update.js. Notice the common mistake he makes and corrects at 34:36—forgetting to wrap the data in an updates object! 35:20 - 36:18: After a successful API call, he calls his updateCartDrawer function, which triggers the UI refresh and re-attaches the event listeners, completing the loop.
Putting it all together, the core JavaScript for an update looks like this:
// This function would be inside your event listener for the +/- buttons
async function handleQuantityChange(lineItemKey, newQuantity) {
try {
// Show a loading indicator if you have one
// Step 3: Make the API call
await fetch('/cart/update.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
updates: {
[lineItemKey]: newQuantity // Using computed property names to set the key
}
})
});
// Step 4: Re-render the cart drawer section
await updateCartDrawer(); // This function fetches and replaces the HTML
} catch (error) {
console.error('Error updating cart:', error);
// Handle errors for the user
} finally {
// Hide the loading indicator
}
}
4. Removing an Item from the Cart
With the logic we've just built, removing an item is incredibly simple. As we learned from the documentation, setting a line item's quantity to 0 removes it from the cart.
If a user clicks the "-" button when the quantity is 1, our newQuantity will be 0. Our handleQuantityChange function will send this to the API, Shopify will remove the item, and our updateCartDrawer function will re-render the drawer without the removed item. It works automatically!
If you wanted a separate "Remove" button, its event listener would simply call handleQuantityChange(lineItemKey, 0).
Test your understanding!
You've successfully implemented the quantity update logic. However, you notice a bug: after you change an item's quantity once, the + and - buttons on all items in the cart drawer stop working. Clicking them does nothing.
What is the most likely cause of this issue, and how would you fix it?
Show answer
The most likely cause is that your JavaScript is not re-attaching the event listeners after the cart drawer's HTML has been replaced.
When you call updateCartDrawer() (or a similar function) and replace the content with element.innerHTML = newHTML;, the original DOM nodes for the buttons (and their attached event listeners) are discarded.
The fix is to ensure that after you replace the HTML, you immediately call the function that is responsible for attaching all the necessary event listeners to the new content (e.g., addCartDrawerListeners()). This function would contain the document.querySelectorAll(...) and button.addEventListener(...) logic.
Conclusion
Excellent work! You have now mastered the complete, fundamental lifecycle of cart management in a Shopify theme using the Ajax API.
Key Takeaways:
- API Endpoints: The
/cart/update.jsendpoint is a versatile way to change quantities of one or more line items. - Removal via Quantity: Setting a line item's quantity to
0is the standard method for removing it from the cart via the API. - Targeting Items: Using the
line_item.keypassed via adata-attribute is the most robust way to identify which item to modify. - UI Synchronization: The Section Rendering API (
/?section_id=...) is a powerful pattern for keeping your UI in sync with server state without complex manual DOM manipulation. - Event Listener Persistence: When you dynamically replace HTML content, you must re-attach any event listeners to the new elements.
In our last two lessons, we've focused on the Ajax API, which is designed for client-side interactions within a traditional, Liquid-based theme. In our next lesson, we'll shift gears to explore a different and more powerful API: the Storefront API. We'll learn its purpose, see how it enables "headless commerce," and understand how it differs from the Ajax API we've been using.