Skip to content
Cart Progress Bar For Free Shipping - Free Tutorial
Browse other ways to boost conversion rate & profit

Cart Progress Bar For Free Shipping - Free Tutorial

Want personalized guidance adding this to your store?

Check out our Insiders community: https://www.skool.com/the-prompted

Members of The Prompted community receive a detailed store audit, 1-on-1 guidance for implementing new features, and access to an exclusive theme. You'll also get marketing support, the same tactics we use to spend over $100k/mo on Meta Ads.

---

Adding a Free Shipping progress bar is a really useful customization, and a nice way to boost your Average Order Value. It's surprisingly easy to add to your store, and that’s exactly what we’re doing in this tutorial.

Compatible Themes: This code should work on all free Shopify themes (Dawn, Refresh, Craft, Studio, Publisher, Crave, Origin, Taste, Colorblock, Sense, Ride, Spotlight).

 

1. Add new Theme Code

Create a new snippet cart-progress-bar-custom.liquid

{% if cart != empty %}

  {% assign progressThresholdDollars = settings.cart_progress_threshold %}
  {% assign progressThreshold = progressThresholdDollars | times: 100 %}
  {% assign preGoalMessageTemplate = settings.cart_pre_goal_message %}
  {% assign postGoalMessage = settings.cart_post_goal_message %}

  {% assign cartTotal = cart.total_price %} 
  {% assign progressPercentage = cartTotal | times: 100 | divided_by: progressThreshold %}
  {% if progressPercentage > 100 %}
    {% assign progressPercentage = 100 %}
  {% endif %}
  
  {% assign remainingForGoal = progressThreshold | minus: cartTotal %}
  {% if remainingForGoal < 0 %}
    {% assign remainingForGoal = 0 %}
  {% endif %}
  {% assign remainingForGoalFormatted = remainingForGoal | money %}
  {% assign dynamicPreGoalMessage = preGoalMessageTemplate | replace: '[remainingForGoalFormatted]', remainingForGoalFormatted %}
  
  <div id="cart-progress-wrapper" 
    class="{% if progressPercentage == 100 %}full{% else %}not-full{% endif %}"
    data-threshold="{{ progressThreshold }}" 
    data-pre-goal-message-template="{{ preGoalMessageTemplate | escape }}"
    data-post-goal-message="{{ postGoalMessage | escape }}"
		data-money-format="{{ shop.money_format | escape }}"
  >
    <div class="cart-progress-bar-container">
      <div id="cart-progress-bar" style="width: {{ progressPercentage }}%;"></div>
    </div>
    <div class="goal-message">
      {% if remainingForGoal > 0 %}
        {{ dynamicPreGoalMessage }}
      {% else %}
        {{ postGoalMessage }}
      {% endif %}
    </div>
  </div>
{% endif %}

<style>
  .cart-progress-bar-container {
    width: 100%;
    background-color: #eee;
    border-radius: 10px;
    margin: 2px auto;
    padding: 1px; 
    overflow: hidden; 
  }

  #cart-progress-bar {
    display: block;
    height: 10px;
    background-color: var(--progress-bar-color, {{ settings.cart_progress_bar_color }}); /* Default color */
    border-radius: 2px;
    transition: width 0.5s ease-in-out;
    border: 1px solid var(--progress-bar-border-color, {{ settings.cart_progress_bar_color }}); 
    padding: 5px 0;
    box-sizing: border-box;
  }

  #cart-progress-wrapper.full #cart-progress-bar {
    --progress-bar-color: {{ settings.cart_progress_bar_full_color }};
    --progress-bar-border-color: {{ settings.cart_progress_bar_full_color }};
  }
  
  .goal-message {
    text-align: center;
    margin: 2px auto 10px;
    font-size: 1em;
    color: {{ settings.cart_progress_bar_text_color }};
  }

</style>

Add settings to settings_schema.json

{
      "type": "header",
      "content": "Cart Progress Bar Settings"
    },
    {
      "type": "text",
      "id": "cart_progress_threshold",
      "label": "Progress Threshold",
      "default": "49",
      "info": "Set the cart total required to unlock the goal."
    },
    {
      "type": "text",
      "id": "cart_pre_goal_message",
      "label": "Pre-Goal Message",
      "default": "You're only [remainingForGoalFormatted] away from <strong>FREE SHIPPING</strong>",
      "info": "Message displayed before reaching the goal. Use [remainingForGoalFormatted] to insert the dynamic remaining amount."
    },
    {
      "type": "text",
      "id": "cart_post_goal_message",
      "label": "Post-Goal Message",
      "default": "🎉 Congrats! You've unlocked <strong>FREE SHIPPING</strong>",
      "info": "Message displayed after reaching the goal."
    },
    {
      "type": "color",
      "id": "cart_progress_bar_color",
      "label": "Progress Bar Color",
      "default": "#d53600",
      "info": "Color of the progress bar."
    },
    {
      "type": "color",
      "id": "cart_progress_bar_full_color",
      "label": "Progress Bar Full Color",
      "default": "#d53600",
      "info": "Color of the progress bar when complete."
    },
	  {
	    "type": "color",
	    "id": "cart_progress_bar_text_color",
	    "label": "Progress Bar Text Color",
	    "default": "#333"
	  }

Add a new method in cart.js

Call the new method in updateQuantity


  const updatedCartTotal = parsedState.total_price;
  this.updateProgressBar(updatedCartTotal);

New method

updateProgressBar(cartTotal, itemCount) {
  const progressWrapper = document.getElementById('cart-progress-wrapper');

  const moneyFormat = progressWrapper.dataset.moneyFormat;
  const progressThreshold = parseInt(progressWrapper.dataset.threshold, 10);
  const preGoalMessageTemplate = progressWrapper.dataset.preGoalMessageTemplate;
  const postGoalMessage = progressWrapper.dataset.postGoalMessage;

  const progressBar = document.getElementById('cart-progress-bar');
  const goalMessageElement = document.querySelector('.goal-message');

  if (itemCount === 0 || cartTotal === 0) {
    if (progressWrapper) {
      progressWrapper.style.display = 'none';
    }
    if (goalMessageElement) {
      goalMessageElement.style.display = 'none';
    }
  } else {
    if (progressWrapper) {
      progressWrapper.style.display = 'block'; 
    }
    if (progressBar) {
      progressBar.style.display = 'block';
      const progressPercentage = Math.min((cartTotal / progressThreshold) * 100, 100); 
      progressBar.style.width = `${progressPercentage}%`;

      if (progressPercentage >= 100) {
        progressWrapper.classList.add('full');
      } else {
        progressWrapper.classList.remove('full');
      }
    }
  
    if (goalMessageElement) {
      goalMessageElement.style.display = 'block';
      let remainingForGoal = progressThreshold - cartTotal;
  
      if (remainingForGoal < 0) {
        remainingForGoal = 0;
      }
  
      const remainingAmount = remainingForGoal / 100;
      const remainingAmountFormatted = moneyFormat.replace('{{amount}}', remainingAmount.toFixed(2));
      const preGoalMessage = preGoalMessageTemplate.replace('[remainingForGoalFormatted]', remainingAmountFormatted);

      goalMessageElement.innerHTML = remainingForGoal > 0 ? preGoalMessage : postGoalMessage;
    }
  }
}

Render the code

main-cart-items.liquid

{%- unless settings.cart_type == 'drawer' -%}
	{% render 'cart-progress-bar-custom' %}
{%- endunless -%}

cart-drawer.liquid

{% render 'cart-progress-bar-custom' %}

Troubleshooting

Different currency formats (ex: uses commas like Euros €12,34)

Check the format for your currency (Settings —> Store Details —> Store Currency —> Change formatting)

Untitled

If it’s NOT {{amount}} then update the cart.js code with the value shown. For example, for Euros, it would be {{amount_with_comma_separator}}

Untitled

 

Browse other ways to boost conversion rate & profit