Want personalized guidance adding this to your store?
Check out our Insiders community: https://www.skool.com/the-prompted
Members of The Prompted community receive a detailed store audit, 1-on-1 guidance for implementing new features, and access to an exclusive theme. You'll also get marketing support, the same tactics we use to spend over $100k/mo on Meta Ads.
---
In this tutorial, we’re looking at adding a simple upsell to your shopify cart, like adding the option for shipping protection or gift wrapping.
This is actually an updated version of our previous simple cart customization, but you were asking how we can make the upsell enabled by default, so we’ve added that functionality in.
Compatible Themes: This code should work on all free Shopify themes (Dawn, Refresh, Craft, Studio, Publisher, Crave, Origin, Taste, Colorblock, Sense, Ride, Spotlight).
Add settings to the theme editor
Edit settings_schema.json
{
"name": "Cart Simple Upsell",
"settings": [
{
"type": "checkbox",
"id": "enable_cart_upsell",
"label": "Use Cart Upsell",
"default": false
},
{
"type": "checkbox",
"id": "default_upsell_enabled_in_cart",
"label": "Upsell is in cart by default",
"default": true
},
{
"type": "text",
"id": "cart_upsell_variant_id",
"label": "Product variant ID",
"placeholder": "Enter the upsell product variant ID",
"info": "Note: Products with only the default variant still have a variant ID that differs from product ID."
},
{
"type": "text",
"id": "cart_upsell_toggle_text",
"label": "Upsell Toggle Text",
"default": "Add Upsell Product"
},
{
"type": "select",
"id": "cart_upsell_alignment",
"label": "Upsell Toggle Alignment",
"options": [
{
"value": "left",
"label": "Left"
},
{
"value": "right",
"label": "Right"
}
],
"default": "left"
},
{
"type": "color",
"id": "cart_upsell_toggle_color",
"label": "Upsell Toggle Color",
"default": "#4CAF50"
}
]
}
Modify add to cart for option to add upsell product by default
Edit buy-buttons.liquid
Add data attribute to the product-form
{% if settings.cart_upsell_variant_id and settings.default_upsell_enabled_in_cart %}data-upsell-variant-id="{{ settings.cart_upsell_variant_id }}"{% endif %}
Edit product-form.js
Add to the ProductForm constructor
this.upsellVariantId = this.dataset.upsellVariantId;
Modify the onSubmitHandler fetch
fetch(`${routes.cart_url}.js`)
.then((response) => response.json())
.then((cartState) => {
if (cartState.item_count === 0 && this.upsellVariantId) {
return fetch(`${routes.cart_add_url}`, this.getUpsellProductConfig());
}
})
.then(() => {
return fetch(`${routes.cart_add_url}`, config);
})
Add a new method getUpsellProductConfig
getUpsellProductConfig() {
const upsellFormData = new FormData();
upsellFormData.append('id', this.upsellVariantId);
upsellFormData.append('quantity', 1);
const config = fetchConfig('javascript');
config.headers['X-Requested-With'] = 'XMLHttpRequest';
delete config.headers['Content-Type'];
config.body = upsellFormData;
return config;
}
Add upsell toggle to the cart
Edit main-cart-items.liquid
Replace the cart.js script tag with the following
{% if settings.enable_cart_upsell %}
<script src="{{ 'cart.js' | asset_url }}" defer="defer" data-cart-upsell-variant-id="{{ settings.cart_upsell_variant_id }}"></script>
{% else %}
<script src="{{ 'cart.js' | asset_url }}" defer="defer"></script>
{% endif %}
Edit main-cart-footer.liquid
Add render for custom liquid file
{% render 'cart-upsell-custom' %}
Edit cart-drawer.liquid
Replace the cart.js script tag with the following
{% if settings.enable_cart_upsell %}
<script src="{{ 'cart.js' | asset_url }}" defer="defer" data-cart-upsell-variant-id="{{ settings.cart_upsell_variant_id }}"></script>
{% else %}
<script src="{{ 'cart.js' | asset_url }}" defer="defer"></script>
{% endif %}
Add render for custom liquid file
{% render 'cart-upsell-custom' %}
Add new snippet cart-upsell-custom.liquid
{% if settings.enable_cart_upsell %}
<div class="cart-upsell-toggle-container cart-upsell-toggle-container--{{ settings.cart_upsell_alignment }} {% if cart.item_count == 0 %}hidden{% endif %}">
<label class="cart-upsell-toggle-label">
<input type="checkbox" id="cart-upsell-toggle" class="cart-upsell-toggle" {% if upsell_in_cart %}checked{% endif %}>
<span class="cart-upsell-toggle-slider"></span>
{{ settings.cart_upsell_toggle_text }}
</label>
</div>
<style>
.cart-upsell-toggle-container {
display: flex;
justify-content: {{ settings.cart_upsell_alignment }};
{% if settings.cart_type == 'drawer' %}
border-bottom: .1rem solid rgba(var(--color-foreground), .2);
padding-bottom: 1rem;
{% endif %}
}
.cart-upsell-toggle-container.hidden {
display: none;
}
.cart-upsell-toggle-label {
display: inline-flex;
align-items: center;
cursor: pointer;
}
.cart-upsell-toggle {
position: absolute;
opacity: 0;
height: 0;
width: 0;
}
.cart-upsell-toggle-slider {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
background-color: #ccc;
border-radius: 20px;
margin-right: 10px;
transition: background-color 0.3s ease;
}
.cart-upsell-toggle-slider::before {
content: "";
position: absolute;
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
border-radius: 50%;
transition: transform 0.3s ease;
}
.cart-upsell-toggle:checked + .cart-upsell-toggle-slider {
background-color: {{ settings.cart_upsell_toggle_color }};
}
.cart-upsell-toggle:checked + .cart-upsell-toggle-slider::before {
transform: translateX(20px);
}
#Details-CartDrawer {
margin-top: 0;
}
</style>
{% endif %}
Edit cart.js
Add to CartItems constructor
const cartUpsellToggle = document.getElementById('cart-upsell-toggle');
if (cartUpsellToggle) {
cartUpsellToggle.addEventListener('change', this.onCartUpsellToggle.bind(this));
}
Add to connectedCallback method
if (this.tagName !== 'CART-DRAWER-ITEMS') {
fetch(`${routes.cart_url}.js`)
.then((response) => response.json())
.then((parsedState) => {
this.updateCartUpsellToggleState();
this.updateCartUpsellVisibility(parsedState.item_count);
})
.catch((e) => {
console.error(e);
});
}
Add to onCartUpdate method (cart drawer)
const parsedStateElement = html.querySelector('[data-cart-drawer-state]');
const parsedState = parsedStateElement ? JSON.parse(parsedStateElement.textContent) : null;
this.updateCartUpsellToggleState();
if (parsedState) {
this.updateCartUpsellVisibility(parsedState.item_count);
}
Add to onCartUpdate method (cart page)
const parsedStateElement = html.querySelector('[data-cart-state]');
const parsedState = parsedStateElement ? JSON.parse(parsedStateElement.textContent) : null;
this.updateCartUpsellToggleState();
if (parsedState) {
this.updateCartUpsellVisibility(parsedState.item_count);
}
Add new upsell toggle methods
updateCartUpsellToggleState() {
const cartUpsellToggle = document.getElementById('cart-upsell-toggle');
const scriptTag = document.querySelector('script[data-cart-upsell-variant-id]');
const cartUpsellVariantId = scriptTag ? scriptTag.dataset.cartUpsellVariantId : '';
const cartItems = document.querySelectorAll('.cart-item');
const upsellItem = Array.from(cartItems).find(item => {
const input = item.querySelector('input[data-quantity-variant-id]');
return input && input.getAttribute('data-quantity-variant-id') === cartUpsellVariantId;
});
if (cartUpsellToggle && cartUpsellToggle.checked !== !!upsellItem) {
cartUpsellToggle.checked = !!upsellItem;
}
}
updateCartUpsellVisibility(itemCount) {
const cartUpsellContainer = document.querySelector('.cart-upsell-toggle-container');
if (cartUpsellContainer) {
if (itemCount === 0) {
cartUpsellContainer.classList.add('hidden');
} else {
cartUpsellContainer.classList.remove('hidden');
}
}
}
onCartUpsellToggle(event) {
const scriptTag = document.querySelector('script[data-cart-upsell-variant-id]');
const cartUpsellVariantId = scriptTag ? scriptTag.dataset.cartUpsellVariantId : '';
const isChecked = event.target.checked;
if (isChecked) {
this.addUpsellProduct(cartUpsellVariantId);
} else {
if (!this.removingUpsellProduct) {
this.removingUpsellProduct = true;
this.removeUpsellProduct(cartUpsellVariantId);
if (this.tagName === 'CART-DRAWER-ITEMS') {
event.stopImmediatePropagation();
}
}
}
}
async addUpsellProduct(cartUpsellVariantId) {
const upsellFormData = new FormData();
upsellFormData.append('id', cartUpsellVariantId);
upsellFormData.append('quantity', 1);
const config = fetchConfig('javascript');
config.headers['X-Requested-With'] = 'XMLHttpRequest';
delete config.headers['Content-Type'];
config.body = upsellFormData;
const response = await fetch(`${routes.cart_add_url}`, config);
if (!response.ok) {
const errorText = await response.text();
console.error('Failed to add upsell product:', errorText);
throw new Error('Failed to add upsell product');
}
this.onCartUpdate();
}
async removeUpsellProduct(cartUpsellVariantId) {
const cartItems = document.querySelectorAll('.cart-item');
const upsellItem = Array.from(cartItems).find(item => {
const input = item.querySelector('input[data-quantity-variant-id]');
return input && input.getAttribute('data-quantity-variant-id') === cartUpsellVariantId;
});
if (!upsellItem) {
console.error('Upsell product not found in the cart.');
return;
}
const upsellIndex = upsellItem.querySelector('input[data-index]').dataset.index;
try {
await this.updateQuantity(upsellIndex, 0, null, cartUpsellVariantId);
this.removingUpsellProduct = false;
} catch (error) {
console.error('Error removing upsell product:', error);
this.removingUpsellProduct = false;
}
}
Add to updateQuantity method
this.updateCartUpsellToggleState();
this.updateCartUpsellVisibility(parsedState.item_count);
Edit cart-drawer.js
Add to the open method
const cartDrawerItems = this.querySelector('cart-drawer-items');
if (cartDrawerItems) {
cartDrawerItems.updateCartUpsellToggleState();
cartDrawerItems.updateCartUpsellVisibility();
}