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

Cart Progress Bar v2 - 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.

---

In this tutorial, we'll show you how to add a Cart Progress Bar to your Shopify store. This feature encourages customers to add more items to their cart to unlock free shipping, helping you increase your average order value (AOV).

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

 

 

Create metafield

Create new market metafield

  • Name: Cart Progress Threshold Number
  • Namespace and key: custom.cart_progress_threshold_number
  • Type: Decimal (one value)
  • Minimum value: 0
  • Maximum decimal places: 2

Edit Theme Code

Add settings to settings_schema.json

  {
    "name": "Cart Progress Bar",
    "settings": [
      {
        "type": "paragraph",
        "content": "Cart Progress Thresholds are set in Market Metafields"
      },   
      {
        "type": "checkbox",
        "id": "enable_cart_progress_bar",
        "label": "Enable Cart Progress Bar",
        "default": false
      },
      {
        "type": "text",
        "id": "cart_pre_goal_message",
        "label": "Pre-Goal Message",
        "default": "You're only [remaining_for_goal] away from <strong>FREE SHIPPING</strong>",
        "info": "Message displayed before reaching the goal. Use [remaining_for_goal] 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_background_color",
        "label": "Progress Bar Background Color",
        "default": "#eee",
        "info": "Color of the progress bar background."
      },
      {
        "type": "color",
        "id": "cart_progress_bar_text_color",
        "label": "Progress Bar Text Color",
        "default": "#333"
      }
    ]
  }

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

{% if cart != empty and localization.market.metafields.custom.cart_progress_threshold_number != nil and settings.enable_cart_progress_bar %}

  {% assign progress_threshold_cents = localization.market.metafields.custom.cart_progress_threshold_number | times: 100 %}
  {% assign pre_goal_message_template = settings.cart_pre_goal_message %}
  {% assign post_goal_message = settings.cart_post_goal_message %}
  
  {% assign cart_total_cents = cart.total_price %}
  {% assign progress_fraction = cart_total_cents | times: 1.0 | divided_by: progress_threshold_cents %}
  {% assign progress_percentage = progress_fraction | times: 100 %}
  {% if progress_percentage > 100 %}
    {% assign progress_percentage = 100 %}
  {% endif %}
  {% assign remaining_for_goal = progress_threshold_cents | minus: cart_total_cents %}
  {% if remaining_for_goal < 0 %}
    {% assign remaining_for_goal = 0 %}
  {% endif %}
  
  {% if settings.currency_code_enabled %}
    {% assign remaining_for_goal_formatted = remaining_for_goal | money_with_currency %}
  {% else %}
    {% assign remaining_for_goal_formatted = remaining_for_goal | money %}
  {% endif %}
  
  {% assign dynamic_pre_goal_message = pre_goal_message_template | replace: '[remaining_for_goal]', remaining_for_goal_formatted %}

  <div id="cart-progress-wrapper" 
    class="{% if progress_percentage == 100 %}full{% else %}not-full{% endif %}"
    data-threshold="{{ progress_threshold_cents }}" 
    data-pre-goal-message-template="{{ pre_goal_message_template | escape }}"
    data-post-goal-message="{{ post_goal_message | escape }}"
    data-currency-format="
      {% if settings.currency_code_enabled %}
        {{ shop.money_with_currency_format | escape }}
      {% else %}
        {{ shop.money_format | escape }}
      {% endif %}
    "
  >
    <div class="cart-progress-bar-container">
      <div id="cart-progress-bar" style="width: {{ progress_percentage }}%;"></div>
    </div>
  
    <div class="goal-message">
      {% if remaining_for_goal > 0 %}
        {{ dynamic_pre_goal_message }}
      {% else %}
        {{ post_goal_message }}
      {% endif %}
    </div>
  </div>
{% endif %}

<style>
  .cart-progress-bar-container {
    width: 100%;
    background-color: {{ settings.cart_progress_bar_background_color }};
    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>

Edit main-cart-items.liquid

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

Edit cart-drawer.liquid

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

Add a new method in cart.js

Call the new method in updateQuantity

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

New methods

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

  const currencyFormat = progressWrapper.dataset.currencyFormat;
  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 = this.formatCurrency(currencyFormat, remainingAmount);
      const preGoalMessage = preGoalMessageTemplate.replace('[remaining_for_goal]', remainingAmountFormatted);

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

formatCurrency(currencyFormat, amount) {
  let formattedAmount = '';
  formattedAmount = currencyFormat
    .replace('{{amount}}', amount.toFixed(2)) // Standard with two decimals
    .replace('{{amount_no_decimals}}', amount.toFixed(0)) // No decimals
    .replace('{{amount_with_comma_separator}}', amount.toFixed(2).replace('.', ',')) // Replace period with comma
    .replace('{{amount_no_decimals_with_comma_separator}}', amount.toFixed(0).replace('.', ',')) // No decimals, use comma
    .replace('{{amount_with_apostrophe_separator}}', amount.toFixed(2).replace('.', "'")) // Apostrophe separator
    .replace('{{amount_no_decimals_with_space_separator}}', amount.toFixed(0).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ' ')) // No decimals, space
    .replace('{{amount_with_space_separator}}', amount.toFixed(2).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ' ').replace('.', ',')) // Space separator
    .replace('{{amount_with_period_and_space_separator}}', amount.toFixed(2).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ' ')); // Period and space
  return formattedAmount;
}

Browse other ways to boost conversion rate & profit