How to Create a Mini Cart in WooCommerce

Quick Summary

  • A custom WooCommerce header mini cart uses three parts: a PHP loop of cart items, a jQuery click handler and a PHP Ajax handler.
  • The loop sets $cart_item_key and $product_id for each item, and the remove link passes them to the script, which removes the item from the header list after the Ajax call.
  • The PHP handler returns WooCommerce's standard mini cart, so add other header parts such as a cart count through woocommerce_add_to_cart_fragments to refresh them too.

A mini cart in the site header lets shoppers see what’s in their cart without leaving the page. These three snippets build one for a custom WooCommerce theme: the markup for the cart items, a jQuery handler that removes an item with Ajax, and the PHP function that handles that request.

Step 1: list the cart items in your custom WooCommerce mini cart

Place this where the mini cart should appear, usually in header.php inside a <ul>. It loops through the cart and prints each item’s thumbnail, title, price and quantity, plus a remove link built with the woocommerce_cart_item_remove_link filter.

PHP
<?php
    global $woocommerce;
    $items = $woocommerce->cart->get_cart();

    foreach($items as $cart_item_key => $values): 
        $_product =  wc_get_product( $values['data']->get_id() );
        $product_id = $values['product_id'];
        //product image
        $getProductDetail = wc_get_product( $values['product_id'] );

        $prod_c_img = $getProductDetail->get_image(); 
        $prod_c_title = $_product->get_title();
        $prod_c_quantity = $values['quantity'];
        $price = get_post_meta($values['product_id'] , '_price', true);
        $price = '$'.$price;
?>

<li class="header-cart-cart-item">
    <div class="header-cart-remove">
        <?php echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
            '<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s" data-cart_item_key="%s">×</a>',
            esc_url( WC()->cart->get_remove_url( $cart_item_key ) ),
            esc_html__( 'Remove this item', 'deepsoul' ),
            esc_attr( $product_id ),
            esc_attr( $_product->get_sku() ),
            esc_attr( $cart_item_key )
        ), $cart_item_key ); ?>

    </div>
    <div class="header-cart-image">
        <a href="">
            <?php echo $getProductDetail->get_image('thumbnail'); ?>
        </a>
    </div>
    <div class="header-cart-details">
        <a class="header-cart-product-title" href="">
            <h4><?php echo $prod_c_title; ?></h4>
        </a>
        <p class="header-cart-price-quantity">
            <span class="header-cart-price">
                <?php echo $price; ?>
            </span>
            <span class="header-cart-quantity ms-1">x <?php echo $prod_c_quantity; ?></span>
        </p>
    </div>
</li>
<?php endforeach; ?>
  • The loop reads each item’s key into $cart_item_key and sets $product_id from $values['product_id']. The remove link passes both to the script in its data- attributes, and its href is WooCommerce’s normal remove URL, so it still works without JavaScript.
  • The price is the parent product’s _price meta with a hard-coded $, so variation prices, taxes and your currency settings are ignored.
  • The image and title links have an empty href; add the product URL to make them clickable.

Step 2: remove items from the custom mini cart with Ajax

Add this to your theme’s main JS file. When a remove link inside .header-cart-cart-item is clicked, it shows a loader and posts the product ID and cart item key to WooCommerce’s Ajax URL with the action product_remove. It then replaces the HTML fragments the server returns and removes the item from the header list.

JavaScript
// Ajax delete product from the cart
jQuery(function ($) {
    $(document).on('click', '.header-cart-cart-item a.remove', function (e)
    {
        e.preventDefault();

        var product_id = $(this).attr("data-product_id"),
            cart_item_key = $(this).attr("data-cart_item_key"),
            product_container = $(this).closest('.header-cart-cart-item');

        // Add loader
        product_container.block({
            message: null,
            overlayCSS: {
                cursor: 'none'
            }
        });

        $.ajax({
            type: 'POST',
            dataType: 'json',
            url: wc_add_to_cart_params.ajax_url,
            data: {
                action: "product_remove",
                product_id: product_id,
                cart_item_key: cart_item_key
            },
            success: function(response) {
                if ( ! response || response.error )
                    return;

                var fragments = response.fragments;

                // Replace fragments
                if ( fragments ) {
                    $.each( fragments, function( key, value ) {
                        $( key ).replaceWith( value );
                    });
                }

                // Remove the item from the header list
                product_container.remove();
            }
        });
    });
});

wc_add_to_cart_params and the .block() loader come from WooCommerce’s add-to-cart script, so it must be loaded on the page. WooCommerce loads it when Enable AJAX add to cart buttons on archives is on in its product settings. The loader covers the clicked item’s <li>, which .closest() finds. The code sits inside jQuery(function ($) { ... }) because WordPress loads jQuery in no-conflict mode, where $ isn’t set.

Step 3: handle the request in functions.php

PHP
// Remove product in the cart using ajax
function warp_ajax_product_remove()
{
    // Get mini cart
    ob_start();

    foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item)
    {
        if($cart_item['product_id'] == $_POST['product_id'] && $cart_item_key == $_POST['cart_item_key'] )
        {
            WC()->cart->remove_cart_item($cart_item_key);
        }
    }

    WC()->cart->calculate_totals();
    WC()->cart->maybe_set_cart_cookies();

    woocommerce_mini_cart();

    $mini_cart = ob_get_clean();

    // Fragments and mini cart are returned
    $data = array(
        'fragments' => apply_filters( 'woocommerce_add_to_cart_fragments', array(
                'div.widget_shopping_cart_content' => '<div class="widget_shopping_cart_content">' . $mini_cart . '</div>'
            )
        ),
        'cart_hash' => apply_filters( 'woocommerce_add_to_cart_hash', WC()->cart->get_cart_for_session() ? md5( json_encode( WC()->cart->get_cart_for_session() ) ) : '', WC()->cart->get_cart_for_session() )
    );

    wp_send_json( $data );

    die();
}

add_action( 'wp_ajax_product_remove', 'warp_ajax_product_remove' );
add_action( 'wp_ajax_nopriv_product_remove', 'warp_ajax_product_remove' );

The function removes the matching item, recalculates the totals and returns WooCommerce’s standard mini cart from woocommerce_mini_cart() as the fragment for div.widget_shopping_cart_content. The wp_ajax_ and wp_ajax_nopriv_ hooks make it work for logged-in users and guests.

The script removes the item from your header list itself, but other parts of the header, such as a cart count, aren’t in that response. To refresh them, add them as fragments through the woocommerce_add_to_cart_fragments filter, as in updating the header cart count with Ajax. The handler also doesn’t check a nonce, so consider adding one.

Share on X (opens in a new tab) Share on LinkedIn (opens in a new tab) Share on Facebook (opens in a new tab)

ChatGPT (opens in a new tab) Claude (opens in a new tab) Gemini (opens in a new tab) Perplexity (opens in a new tab) Grok (opens in a new tab)

More Insights

See More