Blog

Shopify Cart Drawer Discount Code: Add It Free (2026 Code + Apps)

Shopify cart drawer with discount code field applying a promo code

Short answer: Shopify's cart drawer has no discount code field by default. Since May 21, 2025 you can add a real one for free — POST the code to /cart/update.js with the discount parameter, then re-render your cart drawer section in the same request so the price updates instantly. Full copy-paste code is below. If you would rather not touch Liquid, Oxify Cart Drawer & Upsells adds the same field, plus auto-apply and free gifts, in about 10 minutes.

Almost every other guide on this topic still tells you to do the old redirect trick — dump the code into /checkout?discount=CODE and hope. That is not applying a discount in the cart. The customer types a code, nothing changes on screen, and they bounce. This guide shows the method that actually updates the price inside the drawer, in real time, before checkout.

Why Shopify's cart drawer has no discount code field

This is architecture, not laziness — and understanding it tells you which method to pick.

Shopify splits the buying flow in two. Your cart drawer is just a front end for the cart (powered by cart.js), which is a dumb container: line items, quantities, raw prices. It historically knew nothing about which codes exist or who qualifies. The checkout is where the smart logic lives — code validation, customer eligibility, stacking rules, taxes, shipping.

Putting a discount input in the cart drawer means forcing the dumb cart to talk to the smart checkout backend before the customer ever reaches checkout. That was genuinely hard until Shopify closed the gap.

On May 21, 2025, Shopify shipped discount support on the /cart/update.js Ajax endpoint. Discount codes are now first-class cart data. It works on every Shopify plan, not just Plus, and it supports multiple codes at once. Most articles ranking for this keyword were written before that change and still teach the redirect hack.

The data behind why this matters

MetricValueSource
Average documented cart abandonment rate70.22%Baymard Institute (meta-analysis of 50 studies)
Abandon because extra costs were too high48%Baymard checkout research
Mobile cart abandonment~78%Device-split industry data
Mobile share of ecommerce traffic60%+Multiple aggregated sources
Typical Shopify add-to-cart rate~4.6%Shopify benchmarks
Typical Shopify conversion rate~1.4%Shopify benchmarks

Look at the last two rows together. Roughly seven out of ten people who liked a product enough to add it never buy it. And because 60%+ of that traffic is mobile, the cart drawer — not the cart page — is the screen where the decision happens.

The single biggest stated reason for abandonment is price shock: "I thought it would be cheaper." A discount field in the drawer attacks that reason directly, because the customer sees the lower number before they commit to checkout instead of hoping it appears later.

4 ways to add a discount code to your Shopify cart drawer

MethodCostSkillPrice updates live in drawer?Works with Shop Pay / Apple Pay?
1. Redirect hackFreeBeginner❌ No❌ No
2. cart/update.js (recommended free)FreeIntermediate✅ Yes✅ Yes
3. Storefront API GraphQLFreeAdvanced✅ Yes✅ Yes
4. Cart drawer appFrom $5.99–$9.99/moNo code✅ Yes✅ Yes

Method 1: The redirect hack (free, 5 minutes — but do not use it)

Redirect hack banner

This is what nearly every competing tutorial publishes. You add an input, and on submit you push the shopper to checkout with the code in the URL:

<form onsubmit="event.preventDefault();
  var c = this.discount.value.trim();
  if (c) window.location.href = '/checkout?discount=' + encodeURIComponent(c);">
  <input type="text" name="discount" placeholder="Promo code">
  <button type="submit">Apply</button>
</form>

Why it fails: nothing changes in the drawer. The shopper gets zero confirmation the code was valid, so they either abandon or arrive at checkout anxious. Worse, Shop Pay, Apple Pay and Google Pay buttons build their own checkout session from the raw cart and ignore the URL parameter entirely — so your express-checkout shoppers silently pay full price. Use this only as a same-day stopgap.

Method 2: The cart/update.js discount API (free, ~30 minutes) — recommended

This is the correct free method in 2026. One request applies the code server-side and returns freshly rendered drawer HTML, so the price updates without a page reload and express checkout buttons respect it.

The core API call

fetch(window.Shopify.routes.root + 'cart/update.js', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ discount: 'SAVE20' })
})
  .then(function (r) { return r.json(); })
  .then(function (cart) { console.log(cart.total_discount); });

Three things the docs make possible that almost nobody uses:

  • Stack multiple codes — comma-separate them, no spaces: { discount: 'SAVE20,FREESHIP' }
  • Remove every code — pass an empty string: { discount: '' }
  • Re-render your drawer in the same request — add a sections parameter and Shopify returns the rendered HTML alongside the cart JSON (up to five sections per call)

Step 1 — Add the input to your cart drawer

In the theme editor, open snippets/cart-drawer.liquid (Dawn and most Dawn-based themes) or sections/cart-drawer.liquid. Paste this above the checkout button, inside the drawer footer:

<div class="ox-discount" data-ox-discount>
  <button type="button" class="ox-discount__toggle" data-ox-toggle
          aria-expanded="false" aria-controls="ox-discount-panel">
    Have a promo code?
  </button>

  <div class="ox-discount__panel" id="ox-discount-panel" hidden>
    <label class="visually-hidden" for="ox-discount-input">Discount code</label>
    <div class="ox-discount__row">
      <input id="ox-discount-input" type="text" name="discount"
             autocomplete="off" autocapitalize="characters" spellcheck="false"
             enterkeyhint="done" placeholder="Enter code">
      <button type="button" class="ox-discount__apply" data-ox-apply>Apply</button>
    </div>
    <p class="ox-discount__msg" data-ox-msg role="status" aria-live="polite"></p>
  </div>

  {%- if cart.cart_level_discount_applications.size > 0 -%}
    <ul class="ox-discount__applied">
      {%- for d in cart.cart_level_discount_applications -%}
        <li>
          <span>{{ d.title }}</span>
          <span>−{{ d.total_allocated_amount | money }}</span>
          <button type="button" data-ox-remove aria-label="Remove {{ d.title }}">×</button>
        </li>
      {%- endfor -%}
    </ul>
  {%- endif -%}
</div>

Note the collapsed "Have a promo code?" toggle. That is deliberate — see UX mistake 1.

Step 2 — The JavaScript that applies it live

Create assets/ox-cart-discount.js and paste this in full. It applies the code, verifies it actually stuck, re-renders the drawer, and handles errors. Set SECTION_ID to your drawer's section id (cart-drawer in Dawn).

(function () {
  var SECTION_ID = 'cart-drawer';       // your cart drawer section id
  var DRAWER_SEL = '#CartDrawer';       // wrapper the section renders into

  function root() { return document.querySelector('[data-ox-discount]'); }

  function setMsg(text, isError) {
    var el = document.querySelector('[data-ox-msg]');
    if (!el) return;
    el.textContent = text || '';
    el.classList.toggle('is-error', !!isError);
  }

  function updateCart(code) {
    return fetch(window.Shopify.routes.root + 'cart/update.js', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
      body: JSON.stringify({ discount: code, sections: SECTION_ID })
    }).then(function (res) {
      if (!res.ok) throw new Error('Cart update failed');
      return res.json();
    });
  }

  // Shopify silently ignores invalid codes, so confirm the discount really applied.
  function discountApplied(cart, code) {
    var apps = cart.cart_level_discount_applications || [];
    var codes = cart.discount_codes || [];
    var wanted = code.trim().toUpperCase();
    var hit = apps.some(function (d) {
      return (d.title || '').toUpperCase() === wanted;
    });
    var listed = codes.some(function (d) {
      return (d.code || '').toUpperCase() === wanted && d.applicable !== false;
    });
    return hit || listed || (cart.total_discount > 0 && apps.length > 0);
  }

  function repaint(cart) {
    var html = cart.sections && cart.sections[SECTION_ID];
    if (!html) { document.dispatchEvent(new CustomEvent('ox:cart:refresh')); return; }
    var fresh = new DOMParser().parseFromString(html, 'text/html');
    var next = fresh.querySelector(DRAWER_SEL);
    var current = document.querySelector(DRAWER_SEL);
    if (next && current) current.innerHTML = next.innerHTML;
  }

  function apply() {
    var input = document.getElementById('ox-discount-input');
    var btn = document.querySelector('[data-ox-apply]');
    if (!input) return;
    var code = input.value.trim();
    if (!code) { setMsg('Enter a code first.', true); return; }

    btn.disabled = true;
    setMsg('Checking…', false);

    updateCart(code)
      .then(function (cart) {
        if (!discountApplied(cart, code)) {
          setMsg('That code is not valid for the items in your cart.', true);
          input.classList.add('ox-shake');
          setTimeout(function () { input.classList.remove('ox-shake'); }, 400);
          return;
        }
        setMsg('Code applied — you saved ' + money(cart.total_discount) + '!', false);
        repaint(cart);
      })
      .catch(function () { setMsg('Something went wrong. Please try again.', true); })
      .then(function () { if (btn) btn.disabled = false; });
  }

  function money(cents) {
    var f = (window.Shopify && Shopify.money_format) || '${{amount}}';
    return f.replace(/\{\{\s*amount\s*\}\}/, (cents / 100).toFixed(2));
  }

  // Auto-apply codes arriving from /discount/CODE email + SMS links
  function autoApply() {
    var stored = sessionStorage.getItem('ox_discount');
    var url = new URLSearchParams(location.search).get('discount');
    var code = url || stored;
    if (!code) return;
    sessionStorage.setItem('ox_discount', code);
    updateCart(code).then(function (cart) {
      if (discountApplied(cart, code)) {
        setMsg(code.toUpperCase() + ' applied — you saved ' + money(cart.total_discount) + '!', false);
        repaint(cart);
      }
    }).catch(function () {});
  }

  document.addEventListener('click', function (e) {
    if (e.target.closest('[data-ox-apply]')) { apply(); }
    if (e.target.closest('[data-ox-remove]')) {
      updateCart('').then(repaint).catch(function () {});
    }
    var toggle = e.target.closest('[data-ox-toggle]');
    if (toggle) {
      var panel = document.getElementById('ox-discount-panel');
      var open = panel.hasAttribute('hidden');
      panel.toggleAttribute('hidden', !open);
      toggle.setAttribute('aria-expanded', String(open));
      if (open) panel.querySelector('input').focus();
    }
  });

  document.addEventListener('keydown', function (e) {
    if (e.key === 'Enter' && e.target.id === 'ox-discount-input') {
      e.preventDefault(); apply();
    }
  });

  document.addEventListener('DOMContentLoaded', autoApply);
})();

Then load it from your theme layout or drawer section:

<script src="{{ 'ox-cart-discount.js' | asset_url }}" defer></script>

Step 3 — Minimum viable CSS (mobile-safe)

This is the part tutorials skip, and it is where mobile conversions leak. The env(safe-area-inset-bottom) padding and 16px font size stop iOS from zooming and from burying the Apply button under the keyboard.

.ox-discount { padding: 12px 0; }
.ox-discount__toggle {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: 14px; text-decoration: underline; color: currentColor;
}
.ox-discount__row { display: flex; gap: 8px; margin-top: 10px; }
.ox-discount__row input {
  flex: 1 1 auto; min-width: 0; height: 44px; padding: 0 12px;
  font-size: 16px; /* prevents iOS zoom-on-focus */
  border: 1px solid rgba(0,0,0,.2); border-radius: 6px;
}
.ox-discount__apply {
  flex: 0 0 auto; height: 44px; padding: 0 18px; border-radius: 6px;
  border: 0; cursor: pointer; font-size: 15px;
}
.ox-discount__msg { margin: 8px 0 0; font-size: 13px; color: #1a7f37; }
.ox-discount__msg.is-error { color: #b42318; }
.ox-discount__applied {
  list-style: none; margin: 10px 0 0; padding: 0; font-size: 14px;
}
.ox-discount__applied li { display: flex; align-items: center; gap: 8px; }
.ox-shake { animation: ox-shake .32s ease; }
@keyframes ox-shake {
  25% { transform: translateX(-4px); } 75% { transform: translateX(4px); }
}
/* keep the drawer footer above the iOS keyboard / home indicator */
.cart-drawer__footer { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }

Verdict: the cleanest free option. Zero monthly cost, no extra scripts, and because the discount lands on the real cart object server-side, Shop Pay and Apple Pay honour it.

Method 3: Storefront API GraphQL (free, advanced)

For headless builds (Hydrogen, Next.js, custom storefronts) the Ajax cart does not exist. You manage discounts on the cart object with the cartDiscountCodesUpdate mutation:

mutation applyDiscount($cartId: ID!, $codes: [String!]!) {
  cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $codes) {
    cart {
      discountCodes { code applicable }
      cost { subtotalAmount { amount } totalAmount { amount currencyCode } }
    }
    userErrors { field message }
  }
}

The response gives you an applicable boolean per code, which is the cleanest validation signal Shopify offers anywhere — use it to drive your success and error states. Trade-off: you need a Storefront API public access token and must track cart IDs in GID format, and the integration is tied to your custom frontend rather than your theme.

Method 4: A cart drawer app (no code)

If you do not want to own Liquid and JavaScript forever — and if you also want auto-apply, free gifts, upsells and a progress bar working together in the same drawer — an app is the practical answer. That is the next section.

Best app for cart drawer discounts: Oxify Cart Drawer & Upsells

Oxify cart drawer showing an applied discount code, savings and free gift progress bar

Oxify Cart Drawer & Upsells is Built for Shopify certified, rated 5.0 on the Shopify App Store, and starts at $9.99/month with a 14-day free trial. It is the option we recommend for stores that run promotions, because it does the two things a plain discount-field app cannot.

1. It applies the offer directly in the cart, not at checkout. The code validates against Shopify in real time, the line total strikes through, and the savings render inside the drawer before the shopper clicks checkout. Stacked codes, automatic discounts and cart-level offers all display correctly.

2. It auto-applies codes from your campaign links. A shopper clicking yourstore.com/discount/SUMMER20 from a Klaviyo email or an SMS blast opens the drawer to see "SUMMER20 applied — you saved $12.40." No typing, no remembering, no chance for a coupon extension to intercept it.

On top of the discount engine you get free gifts with threshold rules, BOGO, volume discounts, AI-driven cart upsells and frequently-bought-together, a free-shipping progress bar, a sticky cart, countdown timers, shipping protection, gift wrap, and post-purchase and thank-you-page upsells — in one app, with one design system, on one script. It supports 8 languages and multi-currency, and integrates with checkout extensions and apps like Judge.me and PageFly.

Why one app beats stacking four: merchants running a separate discount app, gift app, upsell app and progress-bar app typically pay $40–$80/month more, fight JavaScript conflicts between them, and end up with a drawer where four vendors' styling collide. Oxify replaces that stack.

Setup is roughly 10 minutes: install, enable the app embed in your theme, pick your drawer layout, and toggle "Discount code field" on. Nothing to paste, nothing to maintain when you change themes.

Install Oxify Cart Drawer & Upsells →

Other Shopify cart drawer discount code apps compared

Oxify is not the only option, and the right pick depends on whether you want a full drawer or just an input box.

AppRatingPrice fromBest forStackingUpsellsFree gifts
Oxify Cart Drawer & Upsells5.0 (34)$9.99/mo · 14-day trialFull drawer + discounts + marketing auto-apply
Dr Stacked Discounts Codes4.8 (153)Free plan · $4.99/moComplex multi-code stacking🚀 Advanced
Dcart — Discount in Cart4.9 (100)$5.99/mo · 7-day trialBare input field, light footprint✅ Combinations
UpCart — Cart Drawer4.8 (810+)Free plan · $29.99/moPixel-perfect custom CSS control
Cartix Cart Discounts + Upsell5.0 (38)$7.99/mo · 5-day trialBudget slide cart with basic upsells⚡ Basic
DiscountX: Stack Combine Popup5.0 (3)Free plan · $4.49/moCheapest stacking widget
Discount Code Display4.3 (38)$3.99/moShowing shoppers which codes exist⚡ Higher tiers
KartDiscount — Coupon on CartFree plan availableNon-technical quick setup✅ Up to 3 codes
shoplab: Discount Code in Cart5.0 (4)$4.99/mo · 7-day trialMinimal, newest option⚡ Basic
Snap Cart Drawer & Sticky CartFreeZero-budget drawer replacement⚡ Basic⚡ Basic

One correction worth knowing: several guides still ranking for this keyword recommend Dcode — Discount codes in Cart. That app is no longer available on the Shopify App Store, so ignore any tutorial that sends you there. Ratings and pricing above were checked in July 2026 — app store figures move, so confirm before you install.

Injection vs replacement: Dcart, Dr Stacked, DiscountX, shoplab and KartDiscount inject a field into whatever drawer your theme already has — lighter, lower risk, limited features, and visual consistency depends on your theme's CSS. Oxify, Cartix, UpCart and Snap replace the drawer entirely — more features and one coherent design, but you lose custom styling on your original drawer. Preview either type on a duplicate theme before publishing.

Picking in one line each. Want the whole drawer solved — codes, gifts, upsells, auto-apply — with one script and one design? Oxify. Only need stacking logic and nothing else? Dr Stacked (there is a free plan). Only need a box to type a code into? Dcart. Want deep custom CSS and have the budget? UpCart. Want to advertise the codes you already run to shoppers who do not have one? Discount Code Display — but read UX mistake 1 first, because that strategy backfires on most stores.

Where to place the promo code box (and what it should look like)

Placement moves conversion as much as the code that powers it. Four patterns work, in rough order of effectiveness:

  • Collapsed link directly above the checkout button. The default recommendation. It sits exactly where a shopper who has a code goes looking, and stays invisible to everyone else.
  • Inline with the order summary. Put it beside the subtotal so the number it changes is in the same eyeline. Good on cart pages, tighter on mobile drawers.
  • Shaded panel inside the summary block. A light background tint groups the field with the totals instead of letting it float as an orphan control. Useful when your drawer already has upsell and gift modules competing for attention.
  • Promo box paired with a free-shipping progress bar. Strong combination: the code lowers the total, the bar shows how close they are to free shipping, and the natural response is to add one more item rather than check out smaller.

What to avoid: a wide, bold, always-open input at the top of the drawer. It reads as "everyone else is paying less than you," and it is the single fastest way to send a ready-to-buy shopper to a coupon site.

The coupon extension problem this quietly fixes

This is the argument nobody makes, and it is the strongest one.

When there is nowhere to type a code, shoppers do not give up on the discount — they go looking. They open Honey, Capital One Shopping, or RetailMeNot's extension, or they leave your tab to Google "[your brand] coupon code."

Three things go wrong from there. The extension may inject an affiliate code from a coupon site, so a third party gets commission on a sale your email already earned — and your influencer attribution is now wrong. It may apply a deeper discount than you intended, cutting margin for no incremental sale. Or the shopper lands on a coupon farm, gets served competitor ads, and never comes back.

A visible "Have a promo code?" link ends that detour. The shopper uses your code, from your campaign, in your drawer. Auto-apply removes even the typing step. Treat this as margin protection and attribution protection, not a convenience feature.

How cart drawer discounts multiply your marketing

Email. Abandoned-cart and welcome flows both hinge on a code the shopper has to remember. When /discount/WELCOME15 auto-applies on drawer open, the recovery email's promise is visible the second they return instead of two clicks later at checkout.

SMS. Codes have to be short and typed on a phone keyboard — the worst possible input environment. A tappable /discount/FLASH30 link plus auto-apply removes typing entirely, which matters more on SMS than any other channel.

Paid ads. If your creative says "use SUMMER20," and the drawer has no field, you have created a promise your storefront cannot keep. Every shopper who hunts for the input is ROAS you already paid for and are now leaking.

Influencer and affiliate. Codes like SARAH15 are your attribution mechanism. Entered in the drawer, attribution stays clean; hunted down via a browser extension, it gets overwritten by a coupon-site affiliate ID.

Retention. Birthday, loyalty and win-back codes are aimed at people who already like you. Making them fight the interface to redeem a gift is the fastest way to waste the goodwill.

5 UX mistakes that kill discount code conversions

5 UX mistakes infographic banner

1. Showing a big, empty discount box

A prominent empty field tells every shopper without a code that they are overpaying. They leave to find one. Collapse it behind a small "Have a promo code?" link — that targets only people who already have a code and leaves everyone else undistracted. The code in Method 2 does this by default.

2. No visible feedback when a code applies

Do not quietly change one number. Strike through the old total, show the new one in bold, and state the saving in words: "$50.00 → $40.00 — you saved $10.00." People need to feel the discount, not calculate it.

3. Generic error messages (and silent failures)

Never use alert(). Show an inline message under the input with a short shake animation. Be specific: "This code doesn't apply to the items in your cart" beats "Invalid code." Critically — /cart/update.js returns HTTP 200 for a bad code, so if you do not verify the discount actually landed, your UI will cheerfully report success on a code that did nothing. The discountApplied() check above exists for exactly this.

4. Ignoring the mobile keyboard

Tap the input on an iPhone and the keyboard often covers the Apply button, and an input under 16px triggers an automatic zoom that breaks your layout. Browser dev tools do not reproduce either. Test on a real iPhone and a real Android before you publish.

5. Forgetting express checkout buttons

Shop Pay, Apple Pay and Google Pay buttons build their own session from the cart object. Any method that only passes the code as a URL parameter — the redirect hack — is bypassed completely, and those shoppers pay full price without noticing. Methods 2, 3 and a properly built app all write to the real cart, so express checkout inherits the discount.

Developer gotchas

Invalid codes return 200. Shopify does not error on a bad discount code via cart/update.js; it simply does not apply it. Always read the returned cart back and confirm before showing a success state.

Stacking uses arrays, not a single value. Loop cart_level_discount_applications (and the line-level discounts array) rather than checking for one code. Shopify supports combining an order discount with a shipping discount on the same cart — see our guide to stacking discounts without wrecking your margins before you turn combinations on.

Re-render, do not hand-patch prices. Passing sections in the same cart/update.js call returns server-rendered HTML with correct Liquid money formatting for every currency and locale. Hand-formatting totals in JavaScript is where multi-currency stores break.

Fetch fresh cart data on every drawer open. Cached drawer markup will happily show a stale full price to a returning shopper whose discount is still on the server.

Shopify Scripts are gone. Ruby-based Scripts stopped executing on June 30, 2026 (editing was already locked from April 15, 2026). If you were doing cart-level price manipulation with Scripts, that logic must now live in Shopify Functions or a discount app.

Keep a checkout fallback. Session cookies occasionally desync between cart and checkout. Appending ?discount=CODE to your checkout URL as a belt-and-braces fallback costs nothing and prevents the rare vanishing-discount bug.

Will a cart drawer app slow my store down?

Well-built cart drawer apps add roughly 30–150KB of asynchronously loaded JavaScript. For scale, one unoptimised product image is often 500KB–2MB. Apps that replace the drawer also remove your theme's drawer JavaScript, so net impact is frequently near zero.

Measure it rather than guess: run PageSpeed Insights on a product page before installing, note mobile LCP and INP, install, and re-run the same URL. If LCP moves more than ~0.5s or INP more than ~100ms, investigate. The real performance mistake is not one good app — it is stacking three overlapping cart apps that each inject their own drawer. If you are still choosing, our roundup of the best Shopify cart drawer apps compares them on speed as well as features.

Which method should you actually use?

Your situationUse thisWhy
Non-technical, want it live todayOxify Cart DrawerNo code, handles express checkout and stacking, ~10 minutes
Running email / SMS / ad / influencer promosOxify Cart DrawerAuto-applies codes from links and protects attribution
Comfortable in Liquid, zero budgetcart/update.js (Method 2)Free, native, real-time, works on every plan
Headless / Hydrogen storefrontStorefront API (Method 3)Only option outside a Liquid theme; gives an applicable flag
Only need a bare input boxDcartCheapest single-purpose app, light footprint
Complex multi-code stacking rulesDr. StackedPurpose-built for advanced combination logic

FAQ

Can you add a discount code field to a Shopify cart drawer without an app?

Yes. Since May 21, 2025, Shopify's /cart/update.js endpoint accepts a discount parameter, so you can add an input to cart-drawer.liquid and POST the code with JavaScript. It applies in real time with no page reload and works on every Shopify plan, not just Plus. Full copy-paste code is in Method 2 above.

How do I apply a discount code directly in the cart instead of at checkout?

POST { "discount": "YOURCODE" } to /cart/update.js. That writes the code to the real cart object server-side, which is what makes the price update in the drawer and what makes Shop Pay and Apple Pay respect it. Adding a sections parameter to the same request returns freshly rendered drawer HTML so the totals redraw instantly. The old /checkout?discount=CODE redirect does not do this — it only passes the code along at checkout.

Does Shopify support multiple discount codes on one order?

Yes. Pass comma-separated codes with no spaces: { "discount": "SAVE20,FREESHIP" }. Whether they combine depends on the combination rules set on each discount in your Shopify admin — typically one order discount plus one shipping discount. Read them back from cart_level_discount_applications (an array), not a single code field.

How do I remove a discount code from the cart?

POST an empty string: { "discount": "" } to /cart/update.js. This clears all applied codes. To swap one code for another, just send the new code — it replaces the previous set.

Why does my discount code not show in the Shopify cart drawer?

Usually one of three reasons. The code applied but your drawer markup is cached, so re-render the section instead of relying on stale HTML. Or the code was silently rejected — /cart/update.js returns HTTP 200 even for invalid codes, so you must verify the discount actually landed. Or you are checking a single-value field instead of looping the cart_level_discount_applications array.

What is the difference between automatic discounts and discount codes?

Automatic discounts fire themselves when conditions are met ("10% off orders over $50") and need no input from the shopper. Discount codes must be entered or arrive via a /discount/CODE link. Both show up in the cart's discount applications once applied, but only codes need a field in your drawer.

How do I auto-apply a discount code from an email or SMS link?

Send traffic to yourstore.com/discount/SUMMER20. Shopify stores the code in the session. To show it in the drawer, capture the code on landing, keep it in sessionStorage, and POST it to /cart/update.js when the cart loads — the autoApply() function in the Method 2 code does exactly this. Oxify Cart Drawer handles it with a toggle.

Should the discount field be visible or hidden by default?

Hide it behind a small "Have a promo code?" link. A large empty box signals to every shopper without a code that they are paying too much, which sends them off-site to hunt for one. A collapsed field serves the people who already have a code without prompting everyone else.

Will the discount work with Shop Pay, Apple Pay and Google Pay?

Only if the code was written to the cart object. The cart/update.js method, the Storefront API method and well-built apps all do this, so express checkout inherits the discount. The /checkout?discount=CODE redirect hack does not — express buttons build their own session and ignore the URL parameter entirely.

What happened to Shopify Scripts?

Ruby-based Shopify Scripts stopped executing on June 30, 2026, after editing was locked on April 15, 2026. Any cart or checkout price logic that lived in Scripts must now run through Shopify Functions or a discount app. Nothing in the cart drawer methods above depends on Scripts.

Does Shopify have a discount code field in the cart by default?

No. Shopify's native cart drawer and cart page only show line items, subtotal and a checkout button. The discount code field lives on the checkout page only. You have to add one yourself with theme code or an app — nothing in theme settings turns it on.

Is there a free app for adding discount codes to the cart drawer?

Dr Stacked Discounts Codes and DiscountX both offer free plans, and Snap Cart Drawer is free. Free tiers usually cap impressions, styling or the number of stacked codes. If you are comfortable in Liquid, the cart/update.js method in this guide is genuinely free with no caps at all.

What is the best Shopify app for cart drawer discount codes?

Oxify Cart Drawer & Upsells — Built for Shopify certified, rated 5.0, from $9.99/month with a 14-day free trial. It applies codes live in the cart, auto-applies codes from email and SMS links, and bundles free gifts, BOGO, volume discounts, AI upsells, a progress bar, sticky cart and post-purchase offers into one app. For a bare input field with no other features, Dcart at $5.99/month is the lighter option.

Start recovering that revenue

Nearly half of all abandonment traces back to price shock at checkout. Showing the discount in the drawer — where mobile shoppers actually decide — is the most direct fix available, and it takes under an hour either way.

  1. Fastest, most complete: install Oxify Cart Drawer & Upsells and have discounts, auto-apply, free gifts and upsells live in about 10 minutes.
  2. Free route: paste the Method 2 code into your theme. Around 30 minutes of Liquid work, no monthly cost.
  3. Either way: collapse the field behind "Have a promo code?", show savings with a strikethrough, verify the code actually applied before showing success, and test on a real phone.

The 70% abandonment rate is not going to fall on its own — but the shoppers who already have your code, and just cannot find where to type it, are the easiest sales you will ever recover.

Ask AI about Oxify App