Add Quantity Increment and Decrement Buttons to WooCommerce Cart with JavaScript

WooCommerce renders cart quantity fields as plain number inputs, but usability research consistently shows that icon buttons reduce cart abandonment more effectively than typed entries on both desktop and mobile. Adding increment and decrement buttons is a purely client-side enhancement that requires no changes to WooCommerce core — a small jQuery script handles all user interactions. WooCommerce triggers the updated_cart_totals and wc_fragments_refreshed events after every AJAX cart update, which re-renders the cart HTML and removes any dynamically injected elements. Binding button injection to both the initial DOM-ready event and these WooCommerce events ensures the buttons survive cart fragment refreshes without duplicating them. jQuery’s DOM creation API accepts a tag string or a configuration object, making it straightforward to build accessible buttons with proper type attributes that prevent accidental form submission. Triggering a change event on the quantity input after each button click hooks into WooCommerce’s existing update logic without reimplementing cart AJAX from scratch. Automatically clicking the hidden update-cart button on quantity change avoids requiring a separate user action, which is the default WooCommerce behaviour. The min and max attributes on each quantity input are set by WooCommerce based on product stock settings, so reading them before calculating the new value respects inventory constraints. CSS for the buttons should use flex on the parent .quantity wrapper to keep the minus button, input, and plus button visually aligned. The WooCommerce access control post demonstrates another front-end and back-end combination for customising the cart experience. The localStorage guide shows how to persist UI state across page loads, useful for remembering user preferences alongside cart quantities. Testing with WooCommerce’s built-in cart page, the mini-cart widget, and any AJAX add-to-cart flows ensures the buttons work correctly everywhere quantities appear. Enqueue the script with in_footer: true and list jquery and wc-cart as dependencies so it loads after all WooCommerce cart scripts are ready.

Problem: WooCommerce's default cart page renders quantity as a plain text input, which is awkward on touch devices and does not match the +/− button pattern users expect from modern e-commerce sites.

Solution: Inject +/− buttons around each quantity input on DOM ready and after every WooCommerce cart fragment refresh using jQuery, then trigger the native WooCommerce cart update on quantity change.

(function($) {
    function injectQtyButtons() {
        $('.quantity').each(function() {
            if ($(this).find('.qty-btn').length) return;
            var $minus = $('<button>').attr('type', 'button').addClass('qty-btn qty-minus').text('-');
            var $plus  = $('<button>').attr('type', 'button').addClass('qty-btn qty-plus').text('+');
            $(this).find('.qty').before($minus).after($plus);
        });
    }

    $(document).ready(injectQtyButtons);
    $(document).on('updated_cart_totals wc_fragments_refreshed', injectQtyButtons);

    $(document).on('click', '.qty-btn', function() {
        var $input = $(this).closest('.quantity').find('.qty');
        var val    = parseInt($input.val(), 10) || 0;
        var step   = parseInt($input.attr('step'), 10) || 1;
        var min    = parseInt($input.attr('min'),  10) || 0;
        var max    = parseInt($input.attr('max'),  10) || 999;

        if ($(this).hasClass('qty-minus')) {
            val = Math.max(val - step, min);
        } else {
            val = Math.min(val + step, max);
        }

        $input.val(val).trigger('change');
    });

    $(document).on('change', '.woocommerce-cart-form .qty', function() {
        $('[name="update_cart"]').prop('disabled', false).trigger('click');
    });
}(jQuery));

NOTE: Enqueue this script with wp_enqueue_script('qty-buttons', get_template_directory_uri() . '/js/qty-buttons.js', ['jquery', 'wc-cart'], '1.0', true) inside a wp_enqueue_scripts hook so it loads after all WooCommerce cart scripts are ready.