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)
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}}