Shopify Cart Drawer with progress bar, free gift, and product recommendations - custom code without apps

Three Shopify features that normally require three separate apps - a progress bar, an automatic free gift, and product recommendations in the cart - can be built with Custom Liquid and JavaScript in a single solution. No monthly costs, no additional HTTP requests, no performance issues.

For the Shopify shop Bodenstaendig by Arthur, I implemented exactly that. Bodenstaendig sells garden products for home growers - seeds, fertilizer, plants. Previously, the cart ran with the Monk app, which cost monthly fees and noticeably slowed down the page load. Now everything runs via custom code directly in the Dawn theme - faster, cheaper, and precisely tailored to the brand.

In this article, I'll show how the three features work together technically and why custom code is the better solution for many Shopify shops than apps.

┌─────────────────────────────────────┐
│         CART DRAWER                 │
│                                     │
│  ┌─────────────────────────────┐   │
│  │  📊 PROGRESS BAR            │   │
│  │  Only €12.50 until gift     │   │
│  │  [████████░░░░░░░░] 30€  60€│   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │  🛒 CART ITEMS              │   │
│  │  Tomato Seeds     €4.90     │   │
│  │  Organic Fertilizer €12.90  │   │
│  │  ─────────────────────────  │   │
│  │  🎁 Free Seeds    Free      │   │
│  │     (auto-add at €30)       │   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │  💡 YOU MAY ALSO LIKE       │   │
│  │  [Img][Img][Img] →         │   │
│  │  Shopify Recommendations API│   │
│  └─────────────────────────────┘   │
│                                     │
│  ┌─────────────────────────────┐   │
│  │  Checkout  →                │   │
│  └─────────────────────────────┘   │
└─────────────────────────────────────┘

Why Are Shopify Apps a Problem for Performance?

Every Shopify app loads its own JavaScript, its own CSS, and often additional external requests - on every single page load. Three apps for cart features can extend the page load by several hundred milliseconds.

At Bodenstaendig, the situation was typical: The Monk app handled the progress bar and gift logic in the cart. It worked, but it loaded its own script bundle, injected its own styling, and made additional API calls. This added up - especially on mobile devices, where Arthur's target audience primarily browses.

The fundamental problem: Apps run as an external layer on top of the theme. They can't be directly integrated into the theme because they need to work for thousands of different shops. This makes them inevitably more generic and heavier than a custom solution. And they cost monthly - in Bodenstaendig's case about 50 euros/month for features that can be solved once with custom code.

The Hidden Costs

50 euros sounds like a small amount. Over a year, that's 600 euros. Over three years, 1,800 euros. For code that doesn't belong to the shop owner and disappears immediately when the app is cancelled.

What Does the Progress Bar Do in the Cart Drawer?

The progress bar shows the customer in real-time how close they are to two thresholds: At 30 euros, they get a free seed packet, at 60 euros, free shipping.

The bar responds to every change in the cart - adding a product, changing quantity, removing a product. The display updates without a page reload, including an animated transition of the fill width.

Technically, the progress bar consists of three elements: A text area showing the remaining amount ("Only 12.50 euros until a free seed packet"), a visual bar with two markers at 30 euros and 60 euros, and labels below the bar that turn green when the threshold is reached.

The texts change based on status:

  • Under 30 euros: "Only X euros until a free seed packet"
  • Between 30 and 60 euros: "🎁 Free seeds secured! Only X euros until free shipping"
  • From 60 euros: "✅ Free shipping & free seeds secured!"

The initial state is calculated server-side in Liquid, so the bar displays correctly immediately when the cart drawer opens - no flickering, no reloading. All subsequent updates run client-side via JavaScript.

Important

The progress bar calculates the cart value without the gift product. Otherwise, adding the free item would affect the bar - a common bug in app solutions.

How Does the Automatic Free Gift Work?

At a cart value of 30 euros, a free seed packet is automatically added to the cart. If the value falls below 30 euros, it's automatically removed again. The customer doesn't have to do anything.

The gift is a normal Shopify product with a price of 0 euros and status "unlisted" - so it doesn't appear in the shop but is addressable via its Variant ID through the API. When adding, a hidden property _gift: true is included. The underscore before "gift" is important: Shopify doesn't display properties that start with an underscore in the checkout.

Through this property, the system recognizes the gift at every point - when calculating the cart value without the gift, when displaying it in the cart (green background, "🎁 Gift" badge instead of price), and when removing it.

JavaScript
// Add gift via Shopify Cart API
await fetch('/cart/add.js', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        items: [{
            id: GIFT_VARIANT_ID,
            quantity: 1,
            properties: { '_gift': 'true' }
        }]
    })
});

The gift item in the cart looks different from normal products: Green background, no price (instead "Free"), no quantity selector, no delete button - just a 🎁 emoji. The customer sees immediately: This is a gift, it's included, they can't change it.

The biggest technical challenge was avoiding race conditions. When a customer quickly adds or removes multiple products, the AJAX calls can overlap. An isUpdating flag, rate limiting (at least 3 seconds between gift checks), and a debounce timer on cart events prevent the gift from being added twice or mistakenly removed at an intermediate state.

How Do Product Recommendations Work in the Cart Drawer?

The cart drawer shows matching product recommendations below the cart items - based on Shopify's own Product Recommendations API, not through an external app.

The recommendations are loaded with the intent "complementary" - this delivers products that match the current cart contents, based on Shopify's Search & Discovery algorithm. The system iterates through all products in the cart and collects recommendations. Products that are already in the cart are filtered out.

The display is a horizontally scrollable area with product cards: image, title, price, and an "add" button. Arrow buttons on the left and right allow navigation. When clicking "add", the product is added directly to the cart via AJAX - with a hidden property _source: cart_recommendation for later tracking in orders.

JavaScript
// Load recommendations via Shopify Recommendations API
const url = `/recommendations/products?section_id=cart-recommendations&product_id=${productId}&limit=10&intent=complementary`;
const response = await fetch(url);

The trick: The recommendations are rendered as their own Shopify Section (cart-recommendations). This means they use Shopify's template engine and don't need an external API. No additional JavaScript bundle, no monthly costs, no third-party requests.

After every cart change, the recommendations are automatically reloaded. A MutationObserver on the cart items container detects DOM changes by the Dawn theme and triggers a reload. Parallel requests are prevented via a loading flag, with a safety reset after 10 seconds in case something hangs.

How Do the Three Features Work Together?

All three features share a single JavaScript block and respond to the same cart events. This is the crucial difference from three separate apps, which would each bring their own event listeners and their own Cart API calls.

The flow when the cart changes:

  1. The customer adds a product or changes the quantity
  2. Dawn's theme fires a cart:updated event
  3. A central handler waits 800ms (debounce), then fetches the current cart via /cart.js
  4. The progress bar is updated with the new cart value
  5. After another 1,500ms, the gift check is executed - add or remove the gift if necessary
  6. The product recommendations are reloaded in parallel

The debounce and staggered timeouts are important. When a customer quickly clicks "plus" three times, the cart shouldn't be fetched three times, the gift checked three times, and the recommendations loaded three times. Instead, the system waits until the click series is over and then makes a single, clean update pass.

No Flickering

The initial state when opening the cart drawer is calculated server-side in Liquid - progress bar, gift display, and empty cart state are ready immediately. JavaScript only takes over on the first interaction. This prevents the typical "flickering" that occurs with app-based solutions.

What Does the Shop Owner Save Through Custom Code Instead of Apps?

The direct costs are easy to calculate: About 50 euros per month for apps that are now replaced by custom code. That's 600 euros per year - and the code belongs to the shop owner, not an app provider.

But the indirect savings are more important:

Performance: No additional JavaScript bundle from an external app. No additional CSS that shifts the layout. No third-party requests blocking the page load. For a Shopify shop whose target audience is mostly on mobile, every saved second of loading time makes a measurable difference in conversion rate.

Control: Every detail is customizable - colors, texts, thresholds, behavior. If Arthur wants a threshold of 25 euros instead of 30 euros tomorrow, it's one line of code instead of a support ticket with the app provider. If he wants to give away a different product instead of a seed packet, he swaps one Variant ID.

Stability: No app updates that unexpectedly change the design. No risk that the app provider changes their pricing model or discontinues the service. The code lives in the theme, works as long as the shop runs, and is portable to a theme change.

Tracking: The hidden properties _gift and _source: cart_recommendation enable direct attribution in Shopify orders - which products came into the cart as gifts, which via recommendations. This provides data for optimization that most apps don't offer.

App Solution Custom Code
Monthly Costs ~50 euros/month 0 euros
Performance Impact Additional scripts & requests Zero (integrated in theme)
Customizability Limited to app settings Complete
Code Ownership Rental (gone on cancellation) Shop owner's property
Development Time Ready immediately A few days one-time
Annual Costs (3 years) ~1,800 euros One-time development costs

When Is Custom Code Worth It Instead of a Shopify App?

Custom code is worth it when the requirement is clearly defined, the app handles a function that can be solved with Liquid and JavaScript, and the shop is operated long-term.

Custom code is better when:

The function is clearly defined - progress bars, gift logic, cart recommendations are perfect candidates. The app causes performance problems. The shop needs an individual display that can't be achieved with app settings. The monthly app costs add up to more than the one-time development over 6-12 months.

An app is better when:

The function is extremely complex and needs regular updates - e.g., subscription management with dunning, product bundles with complex pricing logic. The shop owner frequently wants to change settings themselves without involving a developer. The app includes A/B testing or analytics that would be complex to rebuild. The shop is just starting and it's not yet clear which features are needed long-term.

Rule of Thumb
  • When an app essentially injects CSS and JavaScript into the shop to change a frontend display, it can almost always be solved better and cheaper with custom code.
  • When an app brings its own backend with database and complex business logic, custom code is often not sensible.

Frequently Asked Questions About Shopify Custom Code and Apps

Can you replace Shopify apps with custom code?

Yes, for many frontend features like progress bars, gift logic, or product recommendations in the cart. Custom Liquid and JavaScript can implement these features directly in the theme - without monthly costs and without performance issues from external scripts.

How much does it cost to replace Shopify apps with custom code?

One-time development costs for typical cart features like progress bars, automatic gifts, and product recommendations range from a few hundred to low four-figure euros. With app costs of 30-80 euros/month, the development usually pays for itself within 6-12 months.

How do you automatically add a free product to a Shopify cart?

Using the Shopify Cart API (/cart/add.js), you can add a product with a price of 0 euros and a hidden property (e.g., _gift: true) via JavaScript. The logic checks on every cart change whether the cart value exceeds a threshold and automatically adds or removes the gift.

How do you build a progress bar in the Shopify cart drawer?

The initial state is calculated server-side in Liquid (no flickering). JavaScript updates the bar on every cart change via the Cart API (/cart.js). The cart value is converted to a percentage of the target value and applied as CSS width to the fill element.

How do product recommendations work in the Shopify cart drawer?

Shopify's own Product Recommendations API (/recommendations/products) provides matching products based on the cart contents. The recommendations are rendered as a Shopify Section and loaded via AJAX into the cart drawer. No external script needed.

Do Shopify apps slow down the shop?

Yes, many apps load their own JavaScript and CSS files on every page load - even on pages where the app function isn't needed at all. Each additional file increases load time. With multiple apps, the performance impact adds up significantly, especially on mobile devices.

Is Your Shop Being Slowed Down by Apps?

Let's discuss in a free initial consultation which apps can be replaced by custom code - and what that means for performance and costs.

Schedule a Consultation