Skip to content
Free video audit

How to Add a Sticky Add to Cart Bar in Shopify (Without an App)

What a good sticky add to cart bar must do, the 4 bugs most scripts have (installment forms, fast scrolling, variants, sold out), and how to build it into your theme.

On mobile, the Add to Cart button is visible for about one screen. As soon as a shopper scrolls to read the description, size guide or reviews, it's gone. A sticky add to cart bar keeps the buy action one tap away, without an extra app.

What a good sticky bar must do

  1. Appear only after the main button leaves the screen (above the viewport), not while it's still below the fold.
  2. Hide again when the shopper scrolls back up to the buy area.
  3. Stay in sync with the selected variant: image, price, compare-at price and savings.
  4. Mirror the real button: if the variant is sold out, the sticky button must be disabled with the same text.
  5. Use the theme's own cart: clicking it should trigger the same AJAX add-to-cart and cart drawer as the main button.

The 4 bugs most sticky-cart scripts have

1. Picking the wrong form (installments)

Stores showing Shop Pay Installments or Klarna have a hidden form.payment-terms that also posts to /cart/add, and it often comes first on the page. A script that grabs the first form[action$="/cart/add"] finds a form with no button, and the sticky bar never appears. Always select the form that contains button[name="add"].

function getMainForm() {
  const forms = document.querySelectorAll('product-form-component form[data-type="add-to-cart-form"], form[action$="/cart/add"]');
  return [...forms].find((form) =>
    form.querySelector('button[name="add"]') &&
    !form.closest('dialog, .quick-add-modal, .product-card')
  );
}

2. Missing fast scrolls

An IntersectionObserver only fires when an element crosses the viewport edge. On a phone, the button often starts below the fold, and a quick flick can jump it straight to above the screen without ever being visible, so no event fires. Back it up with a passive scroll check:

const update = () => {
  const show = anchor.getBoundingClientRect().bottom < 0; // scrolled above the screen
  bar.classList.toggle('is-visible', show);
};
new IntersectionObserver(update).observe(anchor);
window.addEventListener('scroll', update, { passive: true });

3. Variants out of sync

Modern themes (Horizon, Fabric and others) re-render the product section when the variant changes. Don't store a reference to the button once. Re-query it after every change and watch the section with a MutationObserver. Read the selected variant from the form's hidden input[name="id"].

4. Ignoring sold out

Copy the main button's disabled state and its label text. That way "Sold out" or "Unavailable" shows correctly, already translated into your store's language.

Click: reuse the theme's cart

The sticky button shouldn't add to cart by itself. Call mainButton.click(). Your theme then handles the AJAX request, the quantity selector, the cart drawer and the "Added" animation exactly as before. One cart flow, no duplicates.

Want it done for you? I build this natively for your theme, tested on mobile and desktop, in 48 hours. See the fixed-price service or get a free video audit.

Prefer video? Watch my free sticky add to cart tutorial on YouTube.

Not sure what to fix first?

Send your store link (or your idea for a new store). You'll get a free video plan within 24 hours.

Get my free audit