How to Automatically Apply Multiple Discount Codes in Shopify (Step-by-Step Guide)

by Jay B, Shopify Expert

Save time and delight shoppers! This Shopify tutorial shows you how to automatically apply multiple discount codes from a URL and persist them using cookies; perfect for marketing campaigns, collaborations, and special offers.

Note

This tutorial was created using the latest version (3.0.0) of Shopify’s Horizon theme.

The logic may differ slightly if your theme uses a custom cart setup or older architecture.


Creating discount-codes.js for URL-Based Persistent Discounts 🔗

This script automatically applies discount codes found in a URL parameter and saves them in a cookie.
Customers will retain these discounts as they browse your store, even if they leave the cart page.


Step 1: Create the discount-codes.js Asset

File path:
/assets/discount-codes.js

In your Shopify theme editor, navigate to the Assets folder and create a new blank file named discount-codes.js.
Paste the following code inside:

import { fetchConfig } from '@theme/utilities';
import { CartUpdateEvent } from '@theme/events';

function applyDiscounts() {
  // Cookie helpers
  function getCookie(name) {
    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
    return match ? decodeURIComponent(match[2]) : null;
  }

  function setCookie(name, value, days = 1) {
    const expires = new Date(Date.now() + days * 864e5).toUTCString();
    document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`;
  }

  // Get discounts from URL
  const urlParams = new URLSearchParams(window.location.search);
  const discountsFromUrl = urlParams.get('discounts')
    ? urlParams.get('discounts').split(',').map(d => d.trim())
    : [];

  // Get stored discounts
  const cookieDiscounts = getCookie('discount_codes')
    ? getCookie('discount_codes').split(',').map(d => d.trim())
    : [];

  // Check if URL discounts differ from cookie discounts
  const areDifferent =
    discountsFromUrl.length !== cookieDiscounts.length ||
    discountsFromUrl.some(d => !cookieDiscounts.includes(d));

  // If new discounts are in the URL, store them in the cookie
  if (discountsFromUrl.length && areDifferent) {
    setCookie('discount_codes', discountsFromUrl.join(','));
  }

  // If no discounts in URL or cookie, stop
  if (!cookieDiscounts.length && !discountsFromUrl.length) return;

  // Determine final discounts to apply
  const finalDiscounts = discountsFromUrl.length ? discountsFromUrl : cookieDiscounts;

  // Fetch cart and apply missing discounts
  const config = fetchConfig('json', {});
  fetch(Theme.routes.cart_url, { ...config })
    .then(res => res.json())
    .then(cart => {
      const activeDiscounts = (cart.discount_codes || []).map(d => d.code.toLowerCase());
      const missingDiscounts = finalDiscounts.filter(
        d => !activeDiscounts.includes(d.toLowerCase())
      );

      if (missingDiscounts.length) {
        const discountsToApply = [
          ...new Set([...activeDiscounts, ...missingDiscounts.map(d => d.toLowerCase())])
        ];

        // Collect cart section IDs for updated render
        const cartItemsComponents = document.querySelectorAll('cart-items-component');
        const cartItemComponentsSectionIds = Array.from(cartItemsComponents)
          .filter(el => el instanceof HTMLElement && el.dataset.sectionId)
          .map(el => el.dataset.sectionId);

        // Prepare the cart update request
        const updateConfig = fetchConfig('json', {
          body: JSON.stringify({
            discount: discountsToApply.join(','),
            sections: cartItemComponentsSectionIds.join(','),
          }),
        });

        // Send the update request
        fetch(Theme.routes.cart_update_url, updateConfig)
          .then(res => {
            if (!res.ok) throw new Error('Failed to apply discounts');
            return res.json();
          })
          .then(cartData => {
            console.log('Applied missing discounts:', missingDiscounts);
            document.dispatchEvent(
              new CartUpdateEvent({}, 'discount-codes', {
                source: 'discount-codes',
                sections: cartData.sections,
              })
            );
          })
          .catch(err => console.error('Error applying discounts:', err));
      } else {
        console.log('All URL/cookie discounts already active.');
      }
    })
    .catch(err => console.error('Error fetching cart:', err));
}

applyDiscounts();

What’s Happening in This Code

Cookie Helpers (getCookie & setCookie)

These functions allow the script to read and store discount codes in the customer’s browser:

  • getCookie reads existing discounts so we don’t reapply the same ones.
  • setCookie saves new discounts, so they persist as the customer navigates the store.

Reading Discounts from the URL

The script checks if the URL contains a ?discounts=CODE1,CODE2 parameter:

  • Multiple codes are split into an array for easier processing.
  • Trimming ensures no extra spaces cause issues.

Comparing URL Discounts to Cookie Discounts

  • If the discounts in the URL differ from those already in the cookie, the cookie is updated.
  • This prevents unnecessary reapplication of the same discounts.

Determining Which Discounts to Apply

  • finalDiscounts chooses URL discounts first, falling back to the cookie if no URL parameter is present.
  • Ensures new discount links always take priority.

Fetching the Cart and Applying Discounts

  • Uses fetch and Shopify’s AJAX Cart API (Theme.routes.cart_url) to get the current cart.
  • Compares finalDiscounts to cart.discount_codes to see what’s missing.
  • Missing codes are added while preserving any existing cart discounts, and the cart UI is updated dynamically using CartUpdateEvent.

Dispatching the Cart Update Event

  • Triggers Shopify’s cart rendering system to refresh affected sections without a full page reload.
  • This is what makes the discounts appear instantly for the customer.

Top tip

The cookie expires after 1 day by default, but you can increase this duration by changing the days value in setCookie().

Reference: Learn more about Shopify’s AJAX Cart API and the cart/change.js endpoint in the official docs —

Shopify AJAX API: POST /cart/change.js


Step 2: Link the Script in theme.liquid

Open Layout → theme.liquid, scroll to the bottom, and insert this before the closing </body> tag:

<script
  src="{{ 'discount-codes.js' | asset_url }}"
  type="module"
  defer
  fetchpriority="low"
></script>

Note

Using type="module" ensures the ES6 imports (like fetchConfig) are handled correctly.


Final Testing and Usage

After saving all changes and ensuring your discount codes are active and published in Shopify Admin, test with a link like this:

https://your-store-name.myshopify.com/?discounts=SAVE10,SHIPFREE
  • The codes SAVE10 and SHIPFREE will be automatically applied to the cart.
  • They’ll persist until the cookie expires or the user visits another discount link.

Troubleshooting

Discounts not applying?

Make sure all discount codes are active and can be combined in Shopify’s discount settings.

Cookie not persisting?

Some browsers block third-party cookies — test in a private window or increase the expiration time in the script.

Cart not updating?

Confirm your theme supports fetchConfig and CartUpdateEvent.
If not, adjust the cart update logic accordingly.


How This Tutorial Came to Be

The idea for this tutorial was born out of a real-world need I encountered almost a year ago while working on a client store.

Initially, I thought this would be a quick tutorial, but I soon realized my old solution, using the checkout URL /checkout?discount=SAVE10,SHIPFREE no longer worked reliably.

This discovery led me to develop a new, much better solution: a simple, reliable way to automatically apply multiple discount codes via a URL, perfect for marketing campaigns and influencer collaborations.

Shopify doesn’t natively support applying multiple discounts via a single URL, so I created this lightweight JavaScript approach to bridge that gap.

There’s room for further enhancements — for example, it could easily be extended to add products to the cart directly from the link as well.


Summary

You’ve now implemented a lightweight, automatic multi-discount system for Shopify that:

  • Applies discounts directly from URL parameters
  • Persists them across sessions with cookies
  • Updates the cart dynamically without page reloads

Perfect for influencer campaigns, partnerships, and seamless promotional experiences that boost conversions.


🚀 Advanced Customisation & Consulting

You've successfully implemented a persistent, multi-discount system! For merchants looking to turn this feature into a full, high-conversion marketing automation tool, the next step is integrating advanced logic and front-end displays.

These complex integrations require deep knowledge of Shopify's Liquid, JavaScript, and Cart APIs. If you are ready to scale this system, these are the high-impact projects I can help you implement:

1. 🛒 URL-to-Cart Automatic Product Addition

Combine the discount link with product provisioning for a truly seamless campaign experience.

  • The Upgrade: Modifying the JavaScript logic to recognise a second URL parameter (e.g., &adds=SKU1:qty,SKU2:qty).

  • The Logic: Extending the fetch sequence to first use the /cart/add.js endpoint to ensure the necessary products are in the cart before applying the associated discount codes.

  • The Goal: Simplify flash sales or collaboration campaigns by letting customers click one link to get the product, the discount, and a pre-loaded cart—removing all friction.

2. 🪟 Front-End Discount Display & Removal

Give users control and clarity by visually showing which codes were applied and offering a quick way to remove them.

  • The Upgrade: Integrating a Liquid snippet and new JavaScript functions to display active codes in the cart summary.

  • The Logic: Creating a function that renders the applied codes (read from the cookie/cart data) and attaching event listeners to a small "X" icon next to each code. Clicking the "X" triggers a cart update to remove that specific code.

  • The Goal: Improve trust and customer experience by providing transparency over automated discounts and full control over their order totals.

3. 🎁 Required Product Auto-Inclusion

This system ensures that if a customer uses a promotional link that requires a specific product to be in the cart for the discount to work (e.g., "Buy Product A to get 20% off"), the required product is automatically added.

  • The Upgrade: Defining a mapping in the JS where Discount Code X is linked to Required Product Y ({ code: 'BOGO', requires: 'HANDLE' }).

  • The Logic: Before applying the discount, the JavaScript checks the cart for the required product handle. If it's missing, the script automatically adds it using the /cart/add.js endpoint, then applies the associated discount code.

  • The Goal: Eliminate the user error where a customer clicks a discount link but fails to qualify because they forgot to add the qualifying item, guaranteeing successful promotion use and boosting the required item's sales.

👉 Ready to automate your conversion campaigns and prevent checkout errors? Let me know which advanced feature you'd like to implement, and we can discuss a custom setup plan.

More articles

How to Build a Custom Quiz on Shopify Using Liquid + Vanilla JavaScript (Step-by-Step Guide)

Create your own powerful Shopify quiz, step-by-step. Use Liquid + Vanilla JS to guide customers to the right products and boost conversions—no third-party app needed.

Read more

Level Up Your Shopify Store

Is your store underperforming? I specialize in creating robust Shopify solutions that boost conversions and improve user experience. Let's talk about your goals and build a store that truly performs for your business.