
- A sticky add-to-cart bar appears after the original purchase button scrolls above the viewport.
- It can reduce unnecessary scrolling, especially on long mobile product pages.
- It does not guarantee higher conversions; measure its effect on your own store.
- Avoid creating a second independent product form or variant selector.
- Triggering the existing product form preserves subscriptions, properties, quantity rules, and app logic.
- Duplicate the theme and test the feature before publishing it.
- The code below suits many Online Store 2.0 themes, including Dawn-style product forms.
- Theme-specific adjustments may be required if your form uses a different ID or button structure.
A sticky add-to-cart bar keeps the primary purchasing action available after a shopper scrolls beyond the original product form. This can be particularly useful on mobile devices and information-rich product pages where customers review images, specifications, sizing, delivery details, and reviews before deciding to buy.
The feature should make the existing purchasing journey more convenient rather than create a second, conflicting product form. A poorly implemented sticky bar can submit the wrong variant, ignore subscriptions or custom product fields, cover important content, and introduce accessibility problems.
This guide shows you how to create a lightweight sticky add-to-cart feature that uses your theme’s existing product form, variant selection, quantity, selling plan, line-item properties, validation, and cart behaviour.
What Is a Sticky Add-to-Cart Bar?
A sticky add-to-cart bar is a fixed interface that remains near the bottom of the browser after a shopper has moved beyond the main product form.
It may display:
- Product image
- Product title
- Selected variant
- Current price
- Quantity
- Availability
- Add-to-cart button
The most reliable implementation does not need to duplicate all this information. A compact bar containing the product identity and a button can provide the required convenience while leaving variant, quantity, subscription, and customization selections in the main form.
When Is a Sticky Add-to-Cart Useful?
A sticky purchase action is most useful when product pages contain enough content to place the original button far above the shopper’s current position.
Common examples include:
- Fashion products with sizing information
- Furniture with dimensions and delivery details
- Electronics with technical specifications
- Beauty products with ingredients and usage instructions
- Products with extensive customer reviews
- High-consideration products with detailed comparisons
- Mobile product pages with long media galleries
A sticky bar may provide less value when the complete product page already fits within one or two screens or when a theme already contains a built-in persistent purchase button.
Check the active theme’s settings before adding custom code. Installing two sticky add-to-cart features can create duplicated controls and overlapping elements.
Does a Sticky Add-to-Cart Increase Conversions?
A sticky bar can remove a usability obstacle by reducing the need to scroll back to the original form. That does not mean it will improve conversions on every store.
Its effect depends on:
- Product complexity
- Page length
- Mobile traffic
- Existing theme design
- Customer intent
- Sticky-bar placement
- Product-option requirements
- Store performance
- Whether the bar covers important content
- Whether customers understand the selected product state
Measure the feature using:
- Product-page add-to-cart rate
- Add-to-cart events per visitor
- Product-to-checkout rate
- Completed purchases
- Revenue per visitor
- Mobile versus desktop performance
- Sticky-button clicks
- Product-form validation errors
A higher sticky-button click rate is not enough to prove success. The feature should contribute to more valid cart additions and completed purchases without increasing errors.
Why the Sticky Bar Should Use the Existing Product Form
The original implementation creates a second Shopify product form with its own variant and quantity selectors. This appears straightforward, but it can become unreliable.
A Shopify product form may contain more than a variant ID and quantity. Depending on the store, it may also include:
- Selling-plan selections
- Subscription choices
- Line-item properties
- Engraving or personalization fields
- File uploads
- Quantity rules
- Volume pricing
- Bundle data
- Pre-order information
- App-generated inputs
- Product-form validation
- AJAX cart behaviour
Shopify requires line-item properties to be submitted from within the product form. Selling plans also require their selected IDs to accompany the cart request.
If the sticky bar creates a separate form, it must duplicate and continuously synchronize all this state. Any missed field can produce a different cart item from the one the customer configured.
The safer approach is to let customers configure the product through the main form and have the sticky button activate that form’s existing add-to-cart button.
Before Editing Your Shopify Theme
Check for a Built-In Setting
Open:
Shopify admin > Online Store > Themes > Edit theme
Navigate to a product page and review the product-information section settings. Search for options such as:
- Sticky add to cart
- Sticky purchase button
- Quick buy
- Mobile add-to-cart bar
- Floating add to cart
Use the theme’s native feature when it meets your requirements. It is usually better integrated with the product form and theme updates.
Duplicate the Theme
If custom development is required:
- Go to Online Store > Themes.
- Find the theme you want to change.
- Open its actions menu.
- Select Duplicate.
- Give the duplicate a recognizable name.
- Make the following changes in that copy.
For a version-controlled store, create a feature branch and test the change in an unpublished or development theme.
Compatibility Note
The following implementation expects the main product form to use this common ID pattern:
product-form-{{ section.id }}It also expects the main add-to-cart button to contain:
name="add"This pattern is used by Dawn and many themes based on Shopify’s reference architecture, but it is not universal.
Before installing the snippet, inspect your product section and find:
- The main product form ID
- The main add-to-cart button selector
- Whether the theme replaces product-form HTML when variants change
- Whether it uses an AJAX cart or redirects to the cart page
If your theme follows a different structure, update the selectors described later in this guide.
Step 1: Create the Sticky Add-to-Cart Snippet
From the duplicated theme:
- Go to Online Store > Themes.
- Open the theme actions menu.
- Click Edit code.
- Open the
snippetsdirectory. - Click Add a new snippet.
- Name it
sticky-add-to-cart.
Paste the following code:
{% if product != blank and section.settings.enable_sticky_atc %}
<aside
id="StickyAtc-{{ section.id }}"
class="sticky-atc"
data-sticky-atc
data-product-form-id="product-form-{{ section.id }}"
aria-label="Quick purchase"
hidden
>
<div class="sticky-atc__inner page-width">
<div class="sticky-atc__product">
{% if product.featured_media != blank %}
<div class="sticky-atc__media" aria-hidden="true">
{{
product.featured_media
| image_url: width: 120
| image_tag:
widths: '60, 90, 120',
sizes: '52px',
loading: 'lazy',
class: 'sticky-atc__image',
alt: ''
}}
</div>
{% endif %}
<div class="sticky-atc__details">
<p class="sticky-atc__title">
{{ product.title | escape }}
</p>
<p class="sticky-atc__message" data-sticky-atc-message>
{{ 'products.product.choose_options' | t }}
</p>
</div>
</div>
<button
type="button"
class="sticky-atc__button"
data-sticky-atc-button
>
{{ 'products.product.add_to_cart' | t }}
</button>
</div>
</aside>
{% style %}
#StickyAtc-{{ section.id }} {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 40;
padding-bottom: env(safe-area-inset-bottom);
background: rgb(var(--color-background, 255 255 255));
color: rgb(var(--color-foreground, 18 18 18));
border-top: 1px solid rgba(var(--color-foreground, 18 18 18), 0.12);
box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.12);
}
#StickyAtc-{{ section.id }}[hidden] {
display: none;
}
#StickyAtc-{{ section.id }} .sticky-atc__inner {
display: flex;
min-height: 76px;
padding-top: 10px;
padding-bottom: 10px;
align-items: center;
justify-content: space-between;
gap: 20px;
}
#StickyAtc-{{ section.id }} .sticky-atc__product {
display: flex;
min-width: 0;
align-items: center;
gap: 12px;
}
#StickyAtc-{{ section.id }} .sticky-atc__media {
width: 52px;
height: 52px;
flex: 0 0 52px;
overflow: hidden;
border-radius: 4px;
background: rgba(var(--color-foreground, 18 18 18), 0.06);
}
#StickyAtc-{{ section.id }} .sticky-atc__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
#StickyAtc-{{ section.id }} .sticky-atc__details {
min-width: 0;
}
#StickyAtc-{{ section.id }} .sticky-atc__title,
#StickyAtc-{{ section.id }} .sticky-atc__message {
overflow: hidden;
margin: 0;
text-overflow: ellipsis;
white-space: nowrap;
}
#StickyAtc-{{ section.id }} .sticky-atc__title {
font-weight: 600;
}
#StickyAtc-{{ section.id }} .sticky-atc__message {
margin-top: 2px;
font-size: 12px;
opacity: 0.72;
}
#StickyAtc-{{ section.id }} .sticky-atc__button {
min-width: 170px;
min-height: 46px;
padding: 10px 22px;
border: 0;
border-radius: 0;
color: rgb(var(--color-button-text, 255 255 255));
background: rgb(var(--color-button, 18 18 18));
font: inherit;
font-weight: 600;
cursor: pointer;
}
#StickyAtc-{{ section.id }} .sticky-atc__button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
#StickyAtc-{{ section.id }} .sticky-atc__button:focus-visible {
outline: 3px solid currentColor;
outline-offset: 3px;
}
@media screen and (max-width: 749px) {
#StickyAtc-{{ section.id }} .sticky-atc__inner {
min-height: 68px;
padding: 8px 12px;
gap: 10px;
}
#StickyAtc-{{ section.id }} .sticky-atc__media {
display: none;
}
#StickyAtc-{{ section.id }} .sticky-atc__details {
max-width: 42vw;
}
#StickyAtc-{{ section.id }} .sticky-atc__button {
min-width: 0;
min-height: 44px;
padding: 10px 16px;
white-space: nowrap;
}
}
@media (prefers-reduced-motion: no-preference) {
#StickyAtc-{{ section.id }}:not([hidden]) {
animation: sticky-atc-enter 180ms ease-out;
}
@keyframes sticky-atc-enter {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
{% endstyle %}
<script>
(() => {
function initializeStickyAtc(root) {
if (!root || root.dataset.initialized === 'true') {
return;
}
const formId = root.dataset.productFormId;
const productForm = document.getElementById(formId);
if (!productForm) {
console.warn(
`Sticky add to cart: product form "${formId}" was not found.`
);
return;
}
const mainButton = productForm.querySelector('[name="add"]');
const stickyButton = root.querySelector(
'[data-sticky-atc-button]'
);
const message = root.querySelector(
'[data-sticky-atc-message]'
);
if (!mainButton || !stickyButton) {
console.warn(
'Sticky add to cart: the main add-to-cart button was not found.'
);
return;
}
root.dataset.initialized = 'true';
function getMainButtonLabel() {
const labelElement = mainButton.querySelector('span');
return (
labelElement?.textContent.trim() ||
mainButton.textContent.trim() ||
'Add to cart'
);
}
function syncButtonState() {
const label = getMainButtonLabel();
const unavailable =
mainButton.disabled ||
mainButton.getAttribute('aria-disabled') === 'true';
stickyButton.disabled = unavailable;
stickyButton.textContent = label;
if (message) {
message.textContent = unavailable
? label
: 'Uses your selected options';
}
}
function updateVisibility() {
const rect = mainButton.getBoundingClientRect();
/*
* Show only after the original button has moved above
* the viewport. Do not show before customers reach it.
*/
const shouldShow =
rect.bottom < 0 &&
document.visibilityState === 'visible';
root.hidden = !shouldShow;
}
stickyButton.addEventListener('click', () => {
syncButtonState();
if (mainButton.disabled) {
productForm.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
mainButton.focus({ preventScroll: true });
return;
}
/*
* Triggering the existing button preserves the theme's
* variant, quantity, selling-plan, property, validation,
* and AJAX cart behaviour.
*/
mainButton.click();
});
const buttonObserver = new MutationObserver(syncButtonState);
buttonObserver.observe(mainButton, {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['disabled', 'aria-disabled', 'class']
});
const visibilityObserver = new IntersectionObserver(
updateVisibility,
{
threshold: 0
}
);
visibilityObserver.observe(mainButton);
window.addEventListener('resize', updateVisibility, {
passive: true
});
document.addEventListener('visibilitychange', updateVisibility);
syncButtonState();
updateVisibility();
}
const currentRoot = document.getElementById(
'StickyAtc-{{ section.id }}'
);
initializeStickyAtc(currentRoot);
/*
* Shopify can reload a section in the theme editor without
* reloading the complete page.
*/
if (!window.stickyAtcSectionListenerAdded) {
document.addEventListener('shopify:section:load', (event) => {
const roots = event.target.querySelectorAll(
'[data-sticky-atc]'
);
roots.forEach(initializeStickyAtc);
});
window.stickyAtcSectionListenerAdded = true;
}
})();
</script>
{% endif %}Step 2: Render the Snippet in the Product Section
Open the main product section. In Dawn, this is usually:
sections/main-product.liquidAdd the following line near the end of the section markup, before {% schema %}:
{% render 'sticky-add-to-cart', product: product, section: section %}Do not place the render statement inside the schema block. The schema must contain valid JSON only.
Need Help With Shopify Development?
We build fast, custom Shopify stores designed to drive more sales.
If your theme uses another product section filename, locate the file containing the main product form and add the snippet there.
Step 3: Add the Theme-Editor Setting
Inside the settings array of the product section’s schema, add:
{
"type": "checkbox",
"id": "enable_sticky_atc",
"label": "Enable sticky add to cart",
"default": true
}Make sure the preceding schema item has a comma and the final item does not contain an invalid trailing comma.
A simplified example looks like this:
"settings": [
{
"type": "checkbox",
"id": "enable_sticky_atc",
"label": "Enable sticky add to cart",
"default": true
}
]Save the file.
Step 4: Enable the Sticky Bar
- Return to Online Store > Themes.
- Open the duplicated theme in the theme editor.
- Navigate to a product page.
- Select the main product-information section.
- Enable Sticky add to cart.
- Save.
- Preview the product page in a separate tab.
Scroll beyond the main add-to-cart button. The sticky bar should appear only after the original button moves above the screen.
Step 5: Verify Your Product Form ID
If the sticky bar does not initialize, inspect the product section and locate the form ID.
The snippet currently uses:
data-product-form-id="product-form-{{ section.id }}"If your theme uses a different pattern, such as:
productForm-{{ section.id }}update the snippet accordingly:
data-product-form-id="productForm-{{ section.id }}"Also confirm that the original purchase button contains:
name="add"If it uses another selector, update this JavaScript line:
const mainButton = productForm.querySelector('[name="add"]');For example:
const mainButton = productForm.querySelector(
'[data-product-add-button]'
);Theme structures are not universal. Avoid changing selectors without checking the rendered markup.
How the Implementation Works
It Watches the Original Add-to-Cart Button
The IntersectionObserver detects when the main purchase button enters or leaves the viewport.
The bar remains hidden when:
- The original button is visible
- The original button is further down the page
- The browser tab is not active
It appears only after the original button has moved above the viewport.
This avoids showing a second purchase button before customers have reached the product form.
It Mirrors Availability
A MutationObserver watches the original button for changes.
When a customer selects an unavailable variant and the main button becomes disabled, the sticky button also becomes disabled and uses the main button’s current label.
It Activates the Existing Form
The sticky button calls:
mainButton.click();This allows the active theme to handle the cart request using its existing behaviour.
The sticky bar does not need to duplicate:
- Variant ID
- Quantity
- Subscription plan
- Custom product properties
- Cart sections
- Cart-drawer logic
- Error handling
- Loading state
This significantly reduces the risk of the sticky bar adding a product configuration different from the one selected by the customer.
Why Not Include a Second Variant Selector?
A second selector creates two representations of the same product state.
If the customer changes a variant in the main form, the sticky selector must update. If the customer changes the sticky selector, the main product image, price, inventory message, selling plans, pickup availability, and application blocks must also update.
This becomes particularly complex for products using:
- Variant-specific images
- Subscription pricing
- Combined listings
- Volume pricing
- Quantity rules
- Product bundles
- Personalized options
- Back-in-stock applications
Keeping product selection in one form produces a clearer and more reliable experience.
A sticky bar can display the selected variant, but it should derive that information from the main product form rather than own a second selector.
Supporting Required Product Customizations
Some stores require customers to enter information before adding a product:
- Engraving text
- Initials
- Gift messages
- Measurements
- File uploads
- Checkbox agreements
- Bundle selections
Because the sticky button triggers the original form, native validation should still run. However, the validation message may appear near an off-screen field.
For products with required customization, consider one of these approaches:
- Do not show the sticky bar until all required fields are complete.
- Scroll back to the incomplete field when validation fails.
- Change the sticky label to Review options until the configuration is valid.
- Disable the sticky feature on the affected product template.
Test the complete workflow for every custom product type before publishing.
Supporting Subscription Products
Subscription and pre-order products use selling plans. The selected selling-plan ID normally belongs inside the primary product form.
Triggering the existing add-to-cart button is safer than creating a new sticky form because it preserves the selling plan selected through the product page.
Still test:
- One-time purchase
- Every subscription frequency
- Prepaid subscriptions
- Subscription discounts
- Pre-orders
- Try-before-you-buy options
- Variant changes after selecting a plan
The theme and subscription application must update the main product form correctly for the sticky trigger to inherit the selection.
Mobile UX and Accessibility Considerations
Keep the Bar Compact
On small screens, a sticky bar should prioritize:
- Product context
- Current availability
- Purchase button
Avoid placing several dropdowns, prices, quantity controls, and promotional messages into a narrow fixed area.
Respect Safe Areas
The CSS uses:
padding-bottom: env(safe-area-inset-bottom);This helps prevent the bar from colliding with interface areas on devices that have a bottom safe-area inset.
Avoid Covering Other Fixed Elements
Check whether the store already uses:
- Cookie consent banner
- Chat widget
- Accessibility controls
- Bottom navigation
- Mobile browser prompt
- Another promotional bar
Adjust the sticky bar or competing element so that essential controls remain visible.
Need Help With Shopify Development?
We build fast, custom Shopify stores designed to drive more sales.
Maintain a Large Tap Target
The example gives the sticky button a minimum height of 44 pixels, providing a more usable target on touchscreens.
Preserve Focus Visibility
Keyboard users should be able to see when the sticky button has focus. Do not remove the focus outline unless it is replaced with an equally visible alternative.
Do Not Rely on Animation
The purchase function remains available when the customer prefers reduced motion. The entrance animation is enabled only when the browser does not request reduced motion.
Performance Considerations
The feature uses:
- One small product image
- Scoped CSS
IntersectionObserverMutationObserver- One click listener
- No additional cart request
- No scroll event running continuously
This is lightweight, but no custom feature has literally zero cost.
Review its effect on:
- Interaction to Next Paint
- JavaScript execution
- Mobile layout
- Cumulative Layout Shift
- Product-page app conflicts
The product image is lazy-loaded because it appears only after the customer scrolls. If the sticky bar does not need an image, remove the image markup to make it even smaller.
Testing Checklist
Before publishing, test the feature with:
- A product containing one variant
- A product containing several variants
- An unavailable variant
- A completely sold-out product
- Quantity selection
- Subscription plans
- Pre-orders
- Product bundles
- Required line-item properties
- File uploads
- Personalized products
- AJAX cart drawer
- Cart-page redirection
- Dynamic checkout buttons
- Multiple languages
- Multiple currencies
- Mobile and desktop devices
- Keyboard navigation
- Screen readers
- Cookie banners and chat widgets
- Theme-editor preview
- Several product templates
- Shopify accelerated checkout entry points
Also verify that clicking the sticky button adds exactly the same item configuration as clicking the main product-form button.
Common Sticky Add-to-Cart Mistakes
Creating an Independent Product Form
A second form can miss the selected quantity, selling plan, personalization, bundle data, or application fields.
Selecting the First Variant Automatically
The first product variant is not always the customer’s selected variant. It might also be unavailable.
Showing the Bar Immediately
A sticky button should not compete with the original product form before customers reach it. The example appears only after the main button scrolls above the viewport.
Hard-Coding /cart/add.js
Shopify recommends locale-aware URLs for Ajax Cart API requests. This implementation avoids making a separate Ajax request by using the theme’s existing product form.
Claiming the Feature Cannot Affect Performance
Every additional element, image, style, and script has some cost. The objective is to keep that cost small and measure the actual result.
Ignoring Theme Updates
Custom theme edits may need to be reviewed or reapplied when installing a new theme version. Record the modified files and keep the changes in Git.
Overlapping Other Fixed Components
Several fixed elements competing for the bottom of a mobile screen can obscure content and make the page difficult to use.
Failing to Track Business Results
Do not keep the feature solely because it looks convenient. Compare valid cart additions, checkout progression, completed orders, and revenue per visitor.
Frequently Asked Questions
What is a sticky add-to-cart bar on Shopify?
A sticky add-to-cart bar keeps the primary purchase action visible after shoppers scroll beyond the original product form, helping them act without navigating back through a long product page.
Does a sticky add-to-cart feature improve conversions?
It can reduce scrolling and keep purchase intent actionable, particularly on mobile or long product pages. Conversion impact varies, so measure cart additions, checkout progression, completed purchases, and revenue per visitor.
Is the sticky add-to-cart bar mobile-friendly?
Yes, when designed carefully. It should use compact content, an adequate tap target, safe-area spacing, visible focus states, and enough room for cookie, chat, or accessibility controls.
Does this implementation require a Shopify app?
No. The feature can be added through Liquid, CSS, JavaScript, and a theme setting. A compatible app or built-in theme feature may be preferable when ongoing development support is unavailable.
Will the sticky bar work with product variants?
Yes. Rather than creating another variant selector, the sticky button triggers the existing product form. This lets the theme submit the variant currently selected through the main product-page controls.
Does it support Shopify subscriptions and product personalization?
It can, because it uses the existing product form containing selling plans and line-item properties. Each subscription, pre-order, personalization, bundle, and required-field workflow must still be tested individually.
Will a sticky add-to-cart feature slow down Shopify?
The implementation is lightweight, but every additional interface has some performance cost. Measure real product-page performance and remove unnecessary media or logic if it affects responsiveness or customer interactions.
Can I disable the sticky add-to-cart bar later?
Yes. The example adds an Enable sticky add to cart checkbox to the product section. Disable the setting for the applicable template or remove the snippet render to deactivate it.
Conclusion
A sticky add-to-cart feature can make long Shopify product pages easier to use by keeping the purchasing action available after customers review additional information.
The implementation should complement the main product form rather than recreate it. Triggering the existing add-to-cart button preserves the selected variant, quantity, selling plan, line-item properties, validation, and theme-specific cart behaviour.
After installing the feature, test it across real product configurations and devices. Retain it only when it improves the buying experience and produces measurable gains beyond additional button clicks.
Stores with subscriptions, bundles, custom product options, or heavily modified product forms may benefit from having experienced Shopify experts adapt and test the implementation against the active theme.



