Cart
Build a Shopify free shipping progress bar
A drop-in progress bar that shows shoppers how much more they need to spend to unlock free shipping. Pure Liquid + CSS, no JavaScript required, works in any theme.

The finished feature
Here's what you'll build
- 01A progress bar that fills as the cart total grows
- 02A dynamic message that updates from 'spend X more' to 'shipping unlocked'
- 03A merchant-editable threshold (no hard-coded amount)
- 04Liquid-only rendering, so it updates on every page load with no JS
Before you start
- Comfortable editing a Shopify theme in the Liquid editor
- You know what a snippet is and how to render one
Interactive build
Build it yourself, in a real editor.
Build it for real, right here. You'll write the Liquid into a pre-styled Northwind cart, three short steps, and watch the bar fill as you type.
3 steps · 15 minutes · free, no signup
Prefer to read the full written walkthrough first? The step-by-step article has every line of code explained.
In the curriculum
Learn this properly
This project shows you the working code. To actually internalize the patterns behind it (so you can build the next feature without copying), take the matching lessons in the platform. Module 1 is free.
- 01Render product pricing the way production themes doYou can render product data in a theme template using the right Liquid objects and filters: the same patterns Shopify's stock themes use, the same patterns a code reviewer would expect.
- 02Show the currency code alongside the priceYou can choose between `money`, `money_with_currency`, and `money_without_trailing_zeros`, and you understand the multi-market case where the currency code matters.
FAQ
Does this work on Online Store 2.0 (Horizon) themes?
Yes. The snippet uses standard Liquid only and reads from `settings.free_shipping_threshold`, which works on any theme. On Horizon, render it inside `sections/main-cart.liquid` or your cart drawer block.
Will the bar live-update when I AJAX add to cart?
Not on its own; the snippet renders Liquid server-side. To make it live-update, listen for cart-change events in your JS and either re-fetch the rendered snippet via the Sections API or update the `width` attribute and message text directly in the DOM. The latter is faster and avoids the transition-restart gotcha above.
Can I use a metafield instead of a theme setting for the threshold?
Yes. Replace `settings.free_shipping_threshold` with `shop.metafields.your_namespace.threshold` and define the metafield in Admin → Settings → Metafields and metaobjects. This lets you change the threshold without editing the theme, useful for merchants running A/B tests on shipping thresholds.
How do I show the bar in the cart drawer too?
Render the same snippet inside your drawer template. Both renders read from the same `cart.total_price` and `settings.free_shipping_threshold`, so they stay in sync without extra work.
Found a bug, a better pattern, or want to suggest the next project? Email hello@learnshopify.dev.