Shopify cart drawer
not updating.
The customer adds an item, changes quantity, or removes a line, and the cart drawer keeps showing the old state. The cart on the server has updated; the HTML has not. On Dawn 12+ and Horizon this is almost always an app calling /cart/add.js without using the Section Rendering API. Five ranked causes below, a console diagnostic, and a refresher snippet that fixes it in one file.
Symptoms
- Add to cart succeeds but the drawer count and line items show the previous state.
- Quantity stepper in the drawer increments visually but the subtotal doesn't recalculate.
- Remove line button deletes the item server-side (visible after refresh) but the drawer keeps rendering it.
- Cart bubble in the header desyncs from the drawer: one updates, the other doesn't.
- Drawer flashes empty for a frame, then re-renders the old items.
30-second diagnostic
Run this in the console, then add to cart, change quantity, or remove a line. The output tells you which cause below applies.
// Jelonyx · Shopify cart drawer diagnostic
// Paste into devtools console. Then add a product to cart from the page.
(() => {
const drawer = document.querySelector('#cart-drawer, cart-drawer, [id*="CartDrawer"]');
const bubble = document.querySelector('#cart-icon-bubble, [id*="cart-count"], [data-cart-count]');
console.group('%c[jlx] cart drawer check', 'color:#B8D67A;font-weight:600');
console.log('cart drawer element:', drawer ? '✅ found · id=' + drawer.id : '❌ missing');
console.log('cart bubble element:', bubble ? '✅ found · id=' + bubble.id : '❌ missing');
console.groupEnd();
// Watch for cart events the theme might be dispatching
['cart:updated','cart:refresh','on:cart:update','cart:change','cart-update'].forEach((evt) => {
document.addEventListener(evt, (e) => {
console.log('[jlx] event fired →', evt, e.detail || '');
});
});
// Sniff /cart/add.* and /cart/change.* requests in flight
const _fetch = window.fetch;
window.fetch = function (url, opts) {
if (typeof url === 'string' && /\/cart\/(add|change|update|clear)/.test(url)) {
const hasSections = url.includes('sections=') ||
(opts && opts.body && String(opts.body).includes('sections'));
console.log(
'[jlx] cart request →', url,
hasSections ? '· ✅ sections requested' : '· ❌ no sections param'
);
}
return _fetch.apply(this, arguments);
};
console.log('[jlx] fetch hooked. add to cart now.');
})();- Cart drawer element missing → the theme has no cart drawer section, or its ID has been renamed (cause #4).
- Request fires but no
sections=param → an app or theme code is using the legacy add-to-cart endpoint (cause #1). - Sections requested but no event fires → the theme expects a different cart event name (cause #3).
- Request 304 Not Modified → the cart response is being cached by a CDN or service worker (cause #5).
- Sections requested, event fires, drawer still stale → the theme is rendering into the wrong selector (cause #2).
Most likely causes, ranked
- 01An app calls
/cart/add.jswithout re-rendering the drawer.Older sticky ATC, bundle builder, and upsell apps fire the legacy AJAX endpoint and update only the cart bubble. They never request the
cart-drawersection, so the drawer HTML stays frozen on whatever Liquid rendered at page load. Frequency: most common.Fix: in Shopify Admin → Apps, disable apps one at a time. The drawer starts updating when the conflicting app is off. Either contact the app developer for an OS 2.0 fix, replace the app, or install the refresher snippet below. It intercepts every
/cart/add//cart/change//cart/updaterequest and forces a drawer re-render after each one. - 02The theme renders into the wrong selector.
After a theme update or a custom build, the cart drawer's outer element ID may not match what the theme JS targets, e.g. the theme fetches
cart-drawerbut the rendered element isCartDraweror wrapped in a custom block. The fetch succeeds, the response is correct, butdocument.querySelector('#cart-drawer')returns null.Fix: in the theme code editor, open
sections/cart-drawer.liquid(or equivalent) and confirm the outer wrapper hasid="cart-drawer". Then in the theme JS, search forgetSectionsToRenderor'cart-drawer'and confirm the same key is used both for the section ID and the DOM lookup. - 03Theme is listening for a different cart event name.
Older Dawn versions dispatch
cart:updated. Horizon listens foron:cart:update. Some custom themes inventcart:refresh. When an app dispatches one but the drawer listens for another, the cart updates server-side and the drawer never knows.Fix: in
assets/global.jsor the cart drawer's JS, search foraddEventListener('cart:to see which event the drawer listens for. Either change the app to dispatch that event, or use the refresher snippet below. It dispatchescart:updatedafter every change so theme code that listens for it will react. - 04Section ID was renamed or removed.
After a theme migration,
cart-drawermay have been renamed tocart,drawer-cart, or merged into a genericcart-template. Section Rendering API requests for the old name return a 404 or empty body, so the drawer never receives new HTML.Fix: in the theme's
sections/folder, confirm the actual filename. Update every place in theme JS where the old section name is referenced. If you cannot edit the theme JS, set the refresher snippet'sSECTIONSarray to the new names. - 05Cart response cached by CDN or service worker.
A misconfigured PWA service worker or a CDN edge cache may cache
/cart.jsor/cart?sections=...responses. The cart updates server-side, but the browser keeps reading the cached version. Symptom: the drawer updates after a hard refresh but not in normal flow.Fix: add
cache: 'no-store'to all cart fetches (the refresher snippet does this). In Application → Service Workers, unregister and reload to clear a misbehaving worker. If a CDN is in front of the storefront, exclude/cart*from the cache rules.
The refresher (causes #1, #3, #5)
Use this when you cannot remove the conflicting app or rewrite theme JS. It intercepts every cart-mutating fetch, re-renders the cart drawer and cart bubble using the Section Rendering API, and dispatches cart:updated so legacy theme code reacts.
{% comment %}
Jelonyx · Cart drawer refresher
Re-renders the cart drawer + cart bubble after every cart change using
the Shopify Section Rendering API. Use when an installed app updates the
cart but does not refresh the drawer.
Drop into snippets/jlx-cart-refresh.liquid and render once on the layout
(theme.liquid) above </body>.
Source: jelonyx.com/shopify/fix/cart-drawer-not-updating
{% endcomment %}
<script>
(function () {
'use strict';
var SECTIONS = ['cart-drawer', 'cart-icon-bubble'];
var inflight = false;
async function refresh() {
if (inflight) return;
inflight = true;
try {
var res = await fetch('/cart?sections=' + SECTIONS.join(','), {
headers: { Accept: 'application/json' },
cache: 'no-store',
});
if (!res.ok) return;
var data = await res.json();
SECTIONS.forEach(function (key) {
var html = data[key];
if (!html) return;
// The section response is the full <section> wrapper. Find the
// first element inside the live page that matches the same id.
var parsed = new DOMParser().parseFromString(html, 'text/html');
var fresh = parsed.querySelector('#' + key) || parsed.body.firstElementChild;
var live = document.querySelector('#' + key);
if (fresh && live) live.innerHTML = fresh.innerHTML;
});
document.dispatchEvent(new CustomEvent('cart:updated'));
} finally {
inflight = false;
}
}
// Hook every fetch that mutates the cart
var _fetch = window.fetch;
window.fetch = function (url, opts) {
var p = _fetch.apply(this, arguments);
if (typeof url === 'string' && /\/cart\/(add|change|update|clear)/.test(url)) {
p.then(function () { refresh(); });
}
return p;
};
// Also expose for theme code that doesn't use fetch
window.jlxRefreshCartDrawer = refresh;
})();
</script>Render once on the layout: open layout/theme.liquid and add the render tag directly above the closing </body> tag:
{% render 'jlx-cart-refresh' %}When to call a developer
- The drawer is built by an app, not a theme section:the refresher won't help; the app owns the markup. Either replace the app or get the developer to expose a public refresh hook.
- Cart updates but checkout total is wrong: that's not a drawer issue, it's a discount, gift card, or cart attribute problem. Different fix.
- Drawer updates only after a delay: usually a CDN or service worker race condition. Needs hands-on debugging.
- You also have the variant picker bug: see the variant picker fix. Both bugs often share the same culprit app, and fixing one without the other still loses sales.
Need this fixed today?
A broken cart drawer reads as a broken store: customers assume the add-to-cart didn't work and leave. We diagnose, install the right fix, and verify across desktop and mobile in one fixed-scope sprint.