Add a Free Shipping Progress Bar to Shopify Cart Drawer

Tanz - Tanz

Why Add A Free Shipping Progress Bar?

Let’s be real: “Free shipping” is the oldest trick in the ecommerce book — because it still works. But it works better when shoppers see how close they are to earning it. A free shipping progress bar adds visual urgency, encourages higher average order value, and does it without discounts or gimmicks.

⚠️ Heads-up Before You Start

Editing your Shopify theme code requires a basic understanding of HTML, CSS, and JavaScript. Always duplicate your theme to create a backup before making any changes.

Step 1: Set Free Shipping Rule in Shopify Admin

From your Shopify admin:

  • Go to → Settings → Shipping and delivery
  • Under General shipping rates, click Manage rates
  • Add a new rate:
    • Condition: Based on order price
    • Minimum price: 50 (or 100 depending on your threshold)
    • Rate name: Free Shipping
    • Price: 0.00
  • Save the settings

Step 2: Create A Snippet

In your theme:

  • Go to Online Store → Themes → Edit code
  • Under the Snippets folder, create a New file
  • Name it: a-free-shipping-progress-bar.liquid
  • Add the following code and Save it
Copy

<div class="cart-shipping__wrapper"
     style="color: {{ settings.cart_text_color }}; font-size: {{ settings.cart_font_size }}px;"
     role="region"
     aria-live="polite">
  <p class="cart-shipping__numOuter">
    {{ settings.cart_text_before_amount }} 
    <b>
      <span class="cart-shipping__currency"></span>
      <span class="cart-shipping__num">0.00</span>
    </b>
    {{ settings.cart_text_after_amount }}
  </p>

  <p class="cart-shipping__success"
    style="color: {{ settings.cart_success_color }};"
    aria-live="assertive">
    {{ settings.cart_success_text }}
  </p>

  <div class="cart-shippingThreshold__bar"
       style="background-color: {{ settings.cart_bar_bg_color }}; position: relative;">
    <span class="cart-shippingThreshold__progress"
          style="background: linear-gradient(90deg, {{ settings.cart_bar_fill_color_start }}, {{ settings.cart_bar_fill_color_end }});"></span>
  </div>
</div>

<style>
.cart-shipping__wrapper {
  padding: 15px 0;
  width: 100%;
}

.cart-shipping__numOuter,
.cart-shipping__success {
  margin-bottom: 6px;
  font-weight: 600;
  display: none;
}

.cart-shippingThreshold__bar {
  border-radius: 5px;
  height: 8px;
  width: 100%;
  overflow: hidden;
  display: block;
  margin-top: 5px;
}

.cart-shippingThreshold__progress {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 0%;
  border-radius: 5px;
  transition: width 0.5s ease-in-out;
}
</style>

<script>
window.shopUrl = '{{ request.origin }}';
window.routes = {
  cart_add_url: '{{ routes.cart_add_url }}',
  cart_change_url: '{{ routes.cart_change_url }}',
  cart_update_url: '{{ routes.cart_update_url }}',
  cart_url: '{{ routes.cart_url }}',
  predictive_search_url: '{{ routes.predictive_search_url }}',
};

document.addEventListener("DOMContentLoaded", function () {
  const threshold = {{ settings.free_shipping_threshold | default: 50 }} * 100; // cents

  function calculateProgress(currentVal) {
    const progressBars = document.querySelectorAll('.cart-shippingThreshold__progress');
    const progressNums = document.querySelectorAll('.cart-shipping__num');
    const progressOuter = document.querySelectorAll('.cart-shipping__numOuter');
    const successMsgs = document.querySelectorAll('.cart-shipping__success');

    let percent = Math.round((100 * currentVal) / threshold);
    percent = Math.min(Math.max(percent, 0), 100);

    progressBars.forEach(el => el.style.width = percent + "%");

    let remaining = Math.max(0, (threshold - currentVal) / 100);

    if (remaining === 0) {
      successMsgs.forEach(el => el.style.display = 'block');
      progressOuter.forEach(el => el.style.display = 'none');
      tooltip.style.display = 'none';
    } else {
      successMsgs.forEach(el => el.style.display = 'none');
      progressOuter.forEach(el => el.style.display = 'block');

      progressNums.forEach(el => el.textContent = remaining.toFixed(2));

      document.querySelectorAll('.cart-shipping__currency').forEach(el => {
        el.textContent = Shopify.currency.active_symbol || '$';
      });

      if (percent >= 50 && percent < 100) {
        tooltip.style.display = 'block';
        tooltip.textContent = "{{ settings.cart_tooltip_text }}";
      } else {
        tooltip.style.display = 'none';
      }
    }
  }

  function fetchAndUpdate() {
    fetch('/cart.js')
      .then(res => res.json())
      .then(data => calculateProgress(data.total_price));
  }

  fetchAndUpdate();

  document.addEventListener("cart:updated", (e) => {
    if (e.detail && e.detail.cart) {
      calculateProgress(e.detail.cart.total_price);
    } else {
      fetchAndUpdate();
    }
  });

  const cartContainer = document.getElementById("CartDrawer") || document.querySelector(".cart-page");
  if (cartContainer) {
    const observer = new MutationObserver(() => fetchAndUpdate());
    observer.observe(cartContainer, { childList: true, subtree: true });
  }
});
</script>

Step 3: Add Settings in settings_schema.json

In the theme edit code editor:

  • Open: config/settings_schema.json
  • Find the big array [ ... ] where all the settings blocks are listed
  • Scroll to the bottom of that list, just before the last ]
  • Add a comma (,) after the last item } in the list
  • Paste this below code and click Save
Copy

{
  "name": "Free Shipping Progress Bar",
  "settings": [
    {
      "type": "number",
      "id": "free_shipping_threshold",
      "label": "Free shipping ammount",
      "default": 50
    },
    {
      "type": "text",
      "id": "cart_text_before_amount",
      "label": "Text before amount",
      "default": "You're"
    },
    {
      "type": "text",
      "id": "cart_text_after_amount",
      "label": "Text after amount",
      "default": "away from free shipping!"
    },
    {
      "type": "text",
      "id": "cart_success_text",
      "label": "Free shipping success text",
      "default": "You're eligible for Free Shipping!"
    },
    {
      "type": "number",
      "id": "cart_font_size",
      "label": "Text size (px)",
      "default": 16
    },
    {
      "type": "color",
      "id": "cart_text_color",
      "label": "Text color",
      "default": "#000000"
    },
    {
      "type": "color",
      "id": "cart_success_color",
      "label": "Success text color",
      "default": "#0A962B"
    },
    {
      "type": "color",
      "id": "cart_bar_bg_color",
      "label": "Progress bar background color",
      "default": "#EBEBEB"
    },
    {
      "type": "color",
      "id": "cart_bar_fill_color_start",
      "label": "Progress bar gradient start",
      "default": "#000000"
    },
    {
      "type": "color",
      "id": "cart_bar_fill_color_end",
      "label": "Progress bar gradient end",
      "default": "#0A962B"
    }
  ]
}

Step 4: Add Snippet To Your Cart Drawer

In the theme edit code editor:

  • Open: snippets/cart-drawer.liquid
  • Or wherever your cart content lives
  • Paste this right after the opening <form> tag:
Copy

{% render 'a-free-shipping-progress-bar' %}
  • Save and preview your cart drawer or page

Step 5: Customize Free Shipping Progress Bar

In your theme:

  • Go to Online Store → Themes → Customize
  • In the theme editor, click the Theme settings icon
  • Select Free Shipping Progress Bar
  • Adjust the settings as needed (amount, text, colors, bar style, etc.)
  • Click Save to apply your changes

If you skip or do not follow these 5 Steps, the Free Shipping Progress Bar will not work.

Real-World Insight

Brands like Allbirds and Brooklinen use cart progress bars to nudge customers over a spending threshold. Shopify brands implementing this tactic have reported up to 22% lift in AOV without needing any paid upsell apps.

Final Thoughts

Free shipping still converts — but only when customers know they’re close. With this simple Shopify snippet and shipping setup, you can create an engaging, mobile-friendly progress bar that drives revenue without plugins, pop-ups, or custom apps.

Back to blog