In this guide, we will add two buttons on the cart page: one for incrementing and one for decrementing the quantity of each product in the cart. After clicking the buttons, we will update the quantity via Ajax and refresh the cart using Magento’s customerData module.
Prerequisites
Before starting, ensure that you have the following:
- A working Magento 2 instance.
- Basic knowledge of module creation in Magento 2.
Step 1: Create the Module Structure
First, create a new module in your Magento project. The module will handle the increment and decrement logic. Here is the basic structure of the module:
app/code/Vendor/UpdateCart/
├── etc
│ ├── module.xml
├── view
│ └── frontend
│ ├── requirejs-config.js
│ ├── layout
│ └── checkout_cart_item_renderers.xml
│ ├── templates
│ └── item
│ └── default.phtml
│ └── web
│ └── js
│ └── updateCart.js
├── registration.php
└── composer.json
Step 2: Add Increment/Decrement Buttons Next to Each Product
To add the increment (+) and decrement (-) buttons next to each product in the cart, follow these steps:
1. Override the Product Template
You will create a custom template (default.phtml) where the increment and decrement buttons will be placed next to each product. Then, use the layout file checkout_cart_item_renderers.xml to override Magento’s default cart item renderer template.
2. Modify checkout_cart_item_renderers.xml
Create or modify the Vendor/UpdateCart/view/frontend/layout/checkout_cart_item_renderers.xml file to point to your custom template:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.cart.item.renderers.default">
<action method="setTemplate">
<argument name="template" xsi:type="string">Vendor_UpdateCart::cart/item/default.phtml</argument>
</action>
</referenceBlock>
<referenceBlock name="checkout.cart.item.renderers.simple">
<action method="setTemplate">
<argument name="template" xsi:type="string">Vendor_UpdateCart::cart/item/default.phtml</argument>
</action>
</referenceBlock>
</body>
</page>
3. Create default.phtml to Include Buttons
In the file Vendor/UpdateCart/view/frontend/templates/cart/item/default.phtml, you can add your custom HTML for the increment and decrement buttons next to each product item:
<!-- Existing HTML -->
<!-- Added decrement buttons -->
<button type="button" class="qty-btn minus" data-action="decrease">-</button>
<input id="cart-<?= $escaper->escapeHtmlAttr($_item->getId()) ?>-qty"
name="cart[<?= $escaper->escapeHtmlAttr($_item->getId()) ?>][qty]"
data-cart-item-id="<?= $escaper->escapeHtmlAttr($_item->getSku()) ?>"
value="<?= $escaper->escapeHtmlAttr($block->getQty()) ?>"
type="number"
min="0"
size="4"
step="any"
title="<?= $escaper->escapeHtmlAttr(__('Qty')) ?>"
class="input-text qty"
data-validate="{required:true,'validate-greater-than-zero':true}"
data-item-qty="<?= $escaper->escapeHtmlAttr($block->getQty()) ?>"
data-role="cart-item-qty"/>
<!-- Added increment buttons -->
<button type="button" class="qty-btn plus" data-action="increase">+</button>
<!-- Existing HTML -->
This structure will add the increment (+) and decrement (-) buttons next to each product’s quantity in the cart.
Step 3: Create the Frontend JavaScript
We will create a JavaScript file to send the Ajax request for incrementing and decrementing the quantity.
Create the cart-update.js file in view/frontend/web/js/:
require([
'jquery',
'mage/url',
'Magento_Customer/js/customer-data',
'Magento_Checkout/js/action/get-totals'
], function ($, urlBuilder, customerData, getTotalsAction) {
'use strict';
const ACTION_INCREASE = 'increase';
const ACTION_DECREASE = 'decrease';
/**
* Initializes the quantity change events by binding the click event to buttons.
*
* @return {void} Nothing
*/
const initQuantityChangeEvents = () => {
$('.qty-btn').on('click', handleQuantityChange);
};
/**
* Handles quantity change (increase/decrease) when a button is clicked.
*
* @param {Event} event - The click event.
* @return {void} Nothing
*/
const handleQuantityChange = (event) => {
const $button = $(event.currentTarget);
const $input = $button.siblings('input[type="number"]');
const currentValue = parseFloat($input.val());
const action = $button.data('action');
const step = getStep($input);
const min = getMinValue($input);
// Update the quantity based on action type
const newValue = getUpdatedQuantity(action, currentValue, step, min);
$input.val(newValue);
// Refresh the cart grid after quantity change
reloadCartGrid();
};
/**
* Retrieves the step value from input, defaults to 1 if not present.
*
* @param {jQuery} $input - The input element.
* @return {number} The step value.
*/
const getStep = ($input) => {
return parseFloat($input.attr('step')) || 1;
};
/**
* Retrieves the minimum value from input.
*
* @param {jQuery} $input - The input element.
* @return {number} The minimum value.
*/
const getMinValue = ($input) => {
return parseFloat($input.attr('min'));
};
/**
* Updates the quantity based on action ('increase' or 'decrease').
*
* @param {string} action - The action type (increase or decrease).
* @param {number} currentValue - The current quantity.
* @param {number} step - The step size.
* @param {number} min - The minimum allowable value.
* @return {number} The updated quantity value.
*/
const getUpdatedQuantity = (action, currentValue, step, min) => {
if (action === ACTION_INCREASE) {
return currentValue + step;
} else if (action === ACTION_DECREASE && currentValue > min) {
return currentValue - step;
}
return currentValue;
};
/**
* Reloads the cart grid via an AJAX request.
*
* @return {void} Nothing
*/
const reloadCartGrid = () => {
const $form = $('form#form-validate');
$.ajax({
url: $form.attr('action'),
data: $form.serialize(),
showLoader: true,
success: handleCartGridReloadSuccess,
error: handleCartGridReloadError
});
};
/**
* Handles the successful AJAX response for cart grid reload.
*
* @param {Object} res - The response object.
* @return {void} Nothing
*/
const handleCartGridReloadSuccess = (res) => {
const $newContent = $($.parseHTML(res)).find("#form-validate");
replaceCartForm($newContent);
refreshCartUI();
};
/**
* Replaces the current cart form with the new content.
*
* @param {jQuery} $newContent - The new form content.
* @return {void} Nothing
*/
const replaceCartForm = ($newContent) => {
$("#form-validate").replaceWith($newContent);
};
/**
* Refreshes the cart user interface: mini-cart, summary, and event bindings.
*
* @return {void} Nothing
*/
const refreshCartUI = () => {
reloadMiniCart(['cart']);
reloadSummary();
initQuantityChangeEvents(); // Re-bind events to updated elements
};
/**
* Handles the AJAX error during cart grid reload.
*
* @param {Object} xhr - The XMLHttpRequest object.
* @return {void} Nothing
*/
const handleCartGridReloadError = (xhr) => {
console.error("Cart grid reload failed: ", xhr.responseText);
};
/**
* Reloads the mini-cart sections and updates its data.
*
* @param {Array} sections - The sections to reload.
* @return {void} Nothing
*/
const reloadMiniCart = (sections) => {
customerData.reload(sections, true);
};
/**
* Reloads the cart summary section.
*
* @return {void} Nothing
*/
const reloadSummary = () => {
getTotalsAction([], $.Deferred());
};
// Bind the quantity change event on DOM ready
$(document).ready(() => {
initQuantityChangeEvents();
});
});
Step 4: Register the JavaScript Component
In the requirejs-config.js file, register the JavaScript component:
var config = {
map: {
'*': {
updateCart: 'Vendor_UpdateCart/js/updateCart',
}
}
};
Step 5: Include the JavaScript in default.phtml
At the end of your default.phtml file, include the initialization script to ensure that the JavaScript is executed when the cart page is loaded.
<!-- Existing HTML -->
<!-- Include the JS component for cart update functionality -->
<script type="text/x-magento-init">
{
"body": {
"updateCart": {}
}
}
</script>
This will initialize the cartUpdate JavaScript after the cart form has been rendered.
Step 6: Ensure Layout XML Includes form.phtml
Make sure your layout XML (checkout_cart_item_renderers.xml) includes the default.phtml file correctly, so it loads on the cart page.
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.cart.item.renderers.default">
<action method="setTemplate">
<argument name="template" xsi:type="string">Vendor_UpdateCart::cart/item/default.phtml</argument>
</action>
</referenceBlock>
<referenceBlock name="checkout.cart.item.renderers.simple">
<action method="setTemplate">
<argument name="template" xsi:type="string">Vendor_UpdateCart::cart/item/default.phtml</argument>
</action>
</referenceBlock>
</body>
</page>
This will ensure that your custom default.phtml file is used for the cart form and the JavaScript is loaded and initialized properly.

Step 7: Testing the Increment/Decrement Functionality
Once you’ve implemented the changes, it’s important to test them in your Magento 2 environment.
1. Enable Your Module
Run the following commands to enable your module, run setup upgrades, and flush the cache:
bin/magento module:enable Vendor_UpdateCart
bin/magento setup:upgrade
bin/magento cache:clean
2. Clear Browser Cache
Sometimes, JavaScript or CSS changes might be cached in your browser. Ensure to clear the browser cache or open the cart page in an incognito window to avoid old cached versions.
3. Test the Increment/Decrement Buttons
Go to the cart page and test the increment (+) and decrement (-) buttons. Ensure that they update the quantity via AJAX and refresh the necessary sections on the page using the customerData module.
To visually demonstrate how the functionality works, watch the video below for a step-by-step walkthrough of the increment and decrement feature in action:
Conclusion
With this setup, we have added increment and decrement buttons to the cart page, updated the quantity using Ajax, and reloaded the relevant sections of the page via the customerData module. This ensures a smooth user experience without full page reloads.