WooCommerce Quantity Plus Minus Buttons: How to Add Them

Add WooCommerce quantity plus minus buttons beside a classic quantity input using two PHP hooks, a jQuery click handler, and optional CSS. This tutorial covers product pages and the classic cart, including minimum and maximum limits, decimal steps, and common theme conflicts.

The controls change the input value and trigger its change event. They do not replace WooCommerce’s server-side stock validation or guarantee that the cart saves automatically.

Compatibility and Setup

  • Classic quantity inputs: This example relies on WooCommerce’s PHP quantity-input template and its .quantity .qty markup. Check that your theme renders the two hooks used below.
  • Cart and Checkout Blocks: Do not assume these PHP hooks or this jQuery handler customize block-based quantity controls. See the WooCommerce hook compatibility reference.
  • Installation: Test on staging and add steps 1–2 once in a small custom plugin or child theme. See our guide to editing theme files safely. Put step 3 in your stylesheet or Additional CSS.
  • Existing controls: Disable an overlapping theme or plugin implementation before adding a second set of buttons or click handlers.
WooCommerce quantity input with custom plus and minus buttons

Step 1: Add the Plus and Minus Buttons

WooCommerce fires woocommerce_before_quantity_input_field and woocommerce_after_quantity_input_field around the quantity <input> — the perfect hooks for inserting buttons directly before and after it without touching template files.

/*============================================
=== Show plus minus buttons
============================================*/
add_action( 'woocommerce_before_quantity_input_field', 'wpwebguru_quantity_minus' );
function wpwebguru_quantity_minus() 
{
    echo '<button type="button" class="minus" aria-label="' . esc_attr__( 'Decrease quantity', 'wpwebguru' ) . '">−</button>';
}

add_action( 'woocommerce_after_quantity_input_field', 'wpwebguru_quantity_plus' );
function wpwebguru_quantity_plus() 
{
    echo '<button type="button" class="plus" aria-label="' . esc_attr__( 'Increase quantity', 'wpwebguru' ) . '">+</button>';
}

Note: type="button" on both buttons is important — without it, a <button> inside a <form> defaults to type="submit", and clicking it would submit the add-to-cart or cart-update form immediately instead of just adjusting the number. The aria-label attributes are added so screen readers announce “Increase quantity” / “Decrease quantity” instead of just a bare “+” or “−” symbol, which otherwise conveys nothing useful to assistive technology.

Step 2: Make the Buttons Actually Work

The buttons above are purely visual until JavaScript wires up their click behavior — reading the input’s current value, its min/max/step attributes (which WooCommerce sets automatically based on the product’s stock and settings), and updating it accordingly.

/*============================================
=== Trigger update quantity script
============================================*/
add_action( 'wp_footer', 'wpwebguru_cart_quantity_plus_minus_btns' );
function wpwebguru_cart_quantity_plus_minus_btns() 
{
    if ( ! is_product() && ! is_cart() ) {
        return;
    }

    wc_enqueue_js( "
        jQuery( document ).on( 'click', 'button.plus, button.minus', function() {
            var qty = jQuery( this ).closest( '.quantity' ).find( '.qty' );
            var val = parseFloat( qty.val() );
            var max = parseFloat( qty.attr( 'max' ) );
            var min = parseFloat( qty.attr( 'min' ) );
            var step = parseFloat( qty.attr( 'step' ) );

            if ( qty.length !== 1 || qty.is( ':disabled, [readonly], [type=hidden]' ) ) {
                return;
            }
            if ( ! isFinite( min ) ) {
                min = 0;
            }
            if ( ! isFinite( max ) ) {
                max = Infinity;
            }
            if ( ! isFinite( step ) || step <= 0 ) {
                step = 1;
            }
            if ( ! isFinite( val ) ) {
                val = min;
            }

            var next = jQuery( this ).is( '.plus' ) ? val + step : val - step;
            next = Number( next.toFixed( 10 ) );
            next = Math.max( min, Math.min( max, next ) );

            if ( next !== val ) {
                qty.val( next ).trigger( 'change' );
            }
        });
    " );
}

Quantity limits: The handler clamps the next value to the input’s minimum and maximum, including a minimum of zero. It rounds decimal arithmetic to ten decimal places and ignores disabled, read-only, or hidden inputs. Missing or nonnumeric step values use an increment of one; stores with specialized quantity rules should adapt and test this behavior.

Cart updates: The delegated handler continues to work when classic cart markup is replaced. Triggering change lets WooCommerce or your theme respond, but you may still need to click Update cart. Test the actual saved quantity and totals after reloading the cart.

Step 3: Style the Buttons (Optional)

The snippet above adds no CSS of its own, so the buttons will inherit your theme’s default button styling. A minimal starting point to lay them out inline with the number input:

.quantity {
    display: flex;
    align-items: center;
}
.quantity .qty {
    width: 50px;
    text-align: center;
}
.quantity .plus,
.quantity .minus {
    width: 32px;
    height: 32px;
    line-height: 1;
    cursor: pointer;
}
Working plus and minus quantity buttons on WooCommerce product page

Common Mistakes to Avoid

  • A JavaScript syntax error anywhere in the script, — always check the browser console if a feature like this appears to do nothing.
  • Forgetting type="button", which causes the button to submit the surrounding form instead of just adjusting the quantity.
  • Not restricting the script to relevant pages — the is_product() / is_cart() check keeps this from loading site-wide where it isn’t needed, which matters for page-load performance.
  • Assuming this loads in every cart context — the script is restricted to product and cart pages. Mini-carts on other pages and block-based controls need their own compatible integration.

Adding personalization as well as quantity controls? Our custom product fields guide explains how to carry shopper input into the order. Test quantity changes alongside its fee policy so the charge matches your intended per-item or per-unit pricing.

Test the Quantity Buttons

  • Increase and decrease a normal product quantity, then verify the cart receives the selected value.
  • Test at the minimum, at the maximum, and one step below the maximum. The buttons should not push the value beyond the allowed range.
  • If your store permits fractional quantities, test decimal increments and the minimum supported quantity.
  • Test sold-individually products, variation changes, keyboard focus, and cart updates with your theme.
  • If buttons appear but do nothing, check browser-console errors, script caching, and whether the input uses the expected markup.

Wrapping Up

Three pieces make this work: two action hooks to insert the button markup, a small jQuery handler to make them functional, and a bit of CSS to make them look intentional rather than default. If you’re customizing more of the WooCommerce customer experience beyond the quantity input, our guide on adding custom tabs to the My Account page covers a similar pattern of hooking into WooCommerce’s own action/filter system rather than overriding template files.

Leave a Reply

Your email address will not be published. Required fields are marked *

2 replies on “WooCommerce Quantity Plus Minus Buttons: How to Add Them”

  • ·
    August 1, 2026 at 5:47 pm

    […] Sticky "Add to Cart" bars on WooCommerce product pages — keeping the buy button reachable without scrolling back up, and a natural pairing with our guide on custom quantity plus/minus buttons. […]

  • […] also customizing quantity controls elsewhere in the WooCommerce cart/checkout flow, our guide on custom quantity plus/minus buttons follows the same pattern of hooking into WooCommerce’s existing filters and AJAX endpoints […]

The link has been Copied to clipboard!