Shopify Tutorial · Dawn Theme · No App

Free Shipping Progress Bar in Shopify — without any app

In this tutorial, I'll show you how to add a free shipping progress bar to your Shopify cart drawer — completely free, no app required. We'll use Liquid, CSS, and a few lines of JavaScript. Built and tested on the Dawn theme, but it works on any theme with a cart drawer.
Video Tutorial
Step-by-step guide
01
Create a new snippet file

Go to Online Store → Themes → Edit Code → Snippets and create a new file. Name it exactly:

filename
free-shipping-bar.liquid

Then paste the full snippet code below into that file and save:

Code
{%- comment -%} START: Shipping goal tracker {%- endcomment -%}
{%- if cart.requires_shipping -%}
  {%- assign shipping_goal = 1000 -%}
  {%- assign subtotal = cart.items_subtotal_price | divided_by: 100.0 -%}
  {%- assign remaining_amount = shipping_goal | minus: subtotal -%}
  {%- capture goal_message -%}
    Add <strong>{{ remaining_amount | times: 100 | money_without_trailing_zeros }}</strong> to unlock <strong>FREE</strong> shipping!
  {%- endcapture -%}
  {%- assign progress_width = subtotal | times: 100 | divided_by: shipping_goal -%}
  {%- if remaining_amount <= 0 -%}
    {%- assign remaining_amount = 0 -%}
    {%- assign progress_width = 100 -%}
    {%- capture goal_message -%}
      🚚 You've unlocked <strong>FREE</strong> shipping!
    {%- endcapture -%}
  {%- endif -%}
  {%- if progress_width > 100 -%}
    {%- assign progress_width = 100 -%}
  {%- endif -%}

  {% style %}
    .drawer__header { padding-bottom: 0; }
    .free-shipping {
      display: flex; flex-direction: column; gap: 8px;
      background: linear-gradient(135deg, #f8f8f8 0%, #f0f0f0 100%);
      padding: 16px 18px; border-radius: 10px; margin: 10px 0;
      border: 1px solid rgba(0,0,0,0.06);
      box-shadow: 0 1px 4px rgba(0,0,0,0.05); margin-top: 0;
    }
    .free-shipping__text {
      text-align: center; font-size: 13px;
      color: #555; letter-spacing: 0.01em; line-height: 1.5;
    }
    .free-shipping__text strong { color: #111; }
    .free-shipping__bar {
      width: 100%; height: 6px; background-color: #e2e2e2;
      border-radius: 100px; display: block !important;
      position: relative; overflow: hidden;
    }
    .free-shipping__bar::before {
      content: ""; width: 0%; height: 100%;
      background: linear-gradient(90deg, #0BBBB7, #6dd6d4);
      position: absolute; left: 0; top: 0;
      transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
      border-radius: 100px;
    }
    .free-shipping__bar.free-shipping__bar--active::after {
      content: ""; position: absolute;
      top: 0; left: -60%; width: 50%; height: 100%;
      background: rgba(255,255,255,0.4); transform: skewX(-20deg);
      animation: goal-shine 0.9s ease-out forwards; border-radius: 100px;
    }
    @keyframes goal-shine { to { left: 120%; } }
    .free-shipping__bar.free-shipping__bar--active::before {
      width: {{ progress_width }}%;
    }
  {% endstyle %}

  <div class="free-shipping">
    <div class="free-shipping__text">{{ goal_message }}</div>
    <div class="free-shipping__bar"></div>
  </div>
{%- endif -%}

<script>
  document.addEventListener('cart-drawer:opened', function() {
    setTimeout(function() {
      document.querySelector('.free-shipping__bar')
        .classList.add('free-shipping__bar--active');
    }, 100);
  });
  document.addEventListener('cart-drawer:updated', function() {
    setTimeout(function() {
      document.querySelector('.free-shipping__bar')
        .classList.add('free-shipping__bar--active');
    }, 100);
  });
  document.addEventListener('cart-drawer:closed', function() {
    setTimeout(function() {
      document.querySelector('.free-shipping__bar')
        .classList.remove('free-shipping__bar--active');
    }, 100);
  });
</script>
{%- comment -%} END: Shipping goal tracker {%- endcomment -%}
02
Render snippet in cart-drawer.liquid

Open cart-drawer.liquid in your theme code. Search for <cart-drawer-items> and paste the line above it:

Code
{% render 'free-shipping-bar' %}
03
Dispatch update event in cart.js

Open assets/cart.js. Search for updateQuantity and paste code above the updatedValue variable declaration:

Code
document.dispatchEvent(new CustomEvent('cart-drawer:updated'));
04
Dispatch open event in cart-drawer.js

Open assets/cart-drawer.js. Find the open(triggeredBy) function and paste this line inside it:

Code
document.dispatchEvent(new CustomEvent('cart-drawer:opened'));
05
Dispatch close event in cart-drawer.js

In the same cart-drawer.js file, find the close() function and paste:

Code
document.dispatchEvent(new CustomEvent('cart-drawer:closed'));
06
Select Cart Drawer from theme settings

Go to Online Store → Themes → Customize → Theme Settings and set your cart type to Drawer. This is required for the custom events to fire.

💡
If your cart is set to Page or Popup, the events won't fire and the bar won't animate. Make sure it's set to Drawer.
🎉

Save all files and preview

Open your cart drawer, add items to cart, and watch the bar animate toward free shipping. Adjust the shipping_goal variable in the snippet to match your store's threshold.

Available for new projects

Want this built for your store?

I implement this kind of custom Shopify development daily — no apps, clean code, done fast. Tell me what you need and I'll get it done right.

60+ custom stores built
5★ Upwork rating
Reply within 24hrs
Book a Free Call Send a message
NO COMMITMENT · REPLIES WITHIN 24HRS
Need help implementing this? Custom Shopify dev — no apps
Book Free Call →