How Do I Add Product Recommendations to My Shopify Cart Drawer?

How Do I Add Product Recommendations to My Shopify Cart Drawer?

Maximizing every opportunity to increase sales is crucial. One often overlooked yet powerful strategy is utilizing the cart page to showcase recommended products. This guide will walk you through the process of adding product recommendations to your Shopify cart page, potentially boosting your conversion rates and average order value.

Customers who've added items to their cart are actively considering a purchase and they're often receptive to product suggestions aligned with their initial choices or something better. Therefore, It's a good strategy to use cart drawers to grab visitors' attention by showcasing products that can inspire additional purchases.

One of the ways to approach this is by:

  1. Selecting a set of products to be displayed which are either complementary or alternative to the existing cart items and monitoring the visitors' response to these products.
  2. After a period of time, maybe try changing the products to see how the new ones perform until you find the set of products that are giving better results.
  3. Keep the process continuous, as what sells today might not be as popular after a certain period of time.

While working on improving the conversion rate, we came up with something similar of our own and created this!

Here is the link to a dummy store to see a demonstration -
Store Url - https://sahil-teststore.myshopify.com/
Password - tewblo

Loom Rec. - https://www.loom.com/share/bc13f3a48b924b23972083c9dc946b3b?sid=cc6c505e-3b36-431b-ae13-ac9dff6be454

This product-recommended block made it easy to improve the visibility of the products, by making it easy for customers to complete their setup.

Consider an example of clothing, If someone purchased tops it showed them related bottoms and another product of our own recommendation.

Every time people visit the cart drawer in a new session they would see different pairs of recommended products, which improves the chances of your product visibility and better conversion.

Bonus - In order to monitor we integrated it with Slack

How You Can Integrate Product Recommendation to the Cart Drawer of Your Shopify Store

1. Create a javascript file with the name ‘cro-cart_recomendation’ in the theme code asset folder

// Product Collections these are just dummy products replace them with yours
const ProdCollections = {
    s1: {
        tops: {
            title: 'Long Sleeve Swing Shirt',  // product title
            handle: 'long-sleeve-swing',  // product handle
        },
        bottoms: {
            title: 'Cydney Plaid',
            handle: 'cydney-plaid',
        }
    },
    s2: {  
        tops: {
            title: 'Chevron',
            handle: 'chevron',
        },
        bottoms: {
            title: 'Lodge',
            handle: 'lodge-womens-shirt',
        }
    },
    s4: {
        tops: {
            title: 'Red Wing Iron Ranger Boot',
            handle: 'redwing-iron-ranger',
        },
        bottoms: {
            title: 'Scout Backpack',
            handle: 'scout-backpack',
        }
    }
  //if want to add more product replicate the same object given above sn: { tops: {title: '', handle: ''}, bottoms: {title: '', handle: ''} }
};


let cartCheckInterval;

function handleCartDrawerMutation(mutationsList, observer) {
    for (let mutation of mutationsList) {
        if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
            if (mutation.target.classList.contains('active')) {
                startCartCheck();
            } else {
                stopCartCheck();
            }
        }
    }
}

function startCartCheck() {
    stopCartCheck();
    
    checkAndFetchCartItems();
    
    cartCheckInterval = setInterval(() => {
        checkAndFetchCartItems();
    }, 4000);
}

function stopCartCheck() {
    if (cartCheckInterval) {
        clearInterval(cartCheckInterval);
        cartCheckInterval = null;
    }
}

function checkAndFetchCartItems() {
    const form1Div = document.getElementById("form1");
    const form2Div = document.getElementById("form2");

    if (form1Div && form2Div) {
        const form1 = form1Div.querySelector("form");
        const form2 = form2Div.querySelector("form");

        const isForm1Empty = !form1 || form1.children.length === 0;
        const isForm2Empty = !form2 || form2.children.length === 0;

        if (isForm1Empty && isForm2Empty) {
            fetchCartItems();
        } else {
            console.log("At least one form has content:");
            if (!isForm1Empty) console.log("Form 1 has content");
            if (!isForm2Empty) console.log("Form 2 has content");
        }
    } else {
        console.log("Form divs not found");
    }
}
const cartDrawerObserver = new MutationObserver(handleCartDrawerMutation);
const cartDrawer = document.querySelector('cart-drawer');
if (cartDrawer) {
    cartDrawerObserver.observe(cartDrawer, { attributes: true });   
}

// Function to send a message to Slack
function sendCartMessageToSlack() {
    var slackWebhookUrl = ""; // Add your Slack webhook URL here
    var payload = {
        text: "Item Added From Best Sellers we Recommend"
    };

    fetch(slackWebhookUrl, {
        method: "POST",
        body: JSON.stringify(payload)
    })
    .then(function(response) {
        if (!response.ok) {
            throw new Error("Failed to send message to Slack");
        }
    })
    .catch(function(error) {
        console.error("Error:", error);
    });
}

// Function to create an Add to Cart form
function createAddToCartForm(elementId, product) {
    var cartDrawer = document.querySelector('cart-notification') || document.querySelector('cart-drawer');
    var addToCartForm = document.getElementById(elementId);

    if (!addToCartForm) {
        console.error("Element with ID '" + elementId + "' not found.");
        return;
    }

    var form = document.createElement('form');
    form.className = 'form fromSelector';
    form.setAttribute('data-type', 'add-to-cart-form');

    var ImgContainer = document.createElement('a');
    ImgContainer.href = product.url;
    ImgContainer.className = 'bs__image-container';
    ImgContainer.style.overflow = 'hidden';
    ImgContainer.style.height = '120px';
    ImgContainer.style.width = '110px';

    var imageTag = document.createElement('img');
    imageTag.src = product.featured_image;
    imageTag.alt = product.title;
    imageTag.style.objectFit = 'cover';
    imageTag.style.height = '100%';
    imageTag.style.width = '100%';
    imageTag.style.position = 'relative';
    ImgContainer.appendChild(imageTag);
    form.appendChild(ImgContainer);

    if (product && product.variants && product.variants.length > 0) {
        var productIdInput = document.createElement('input');
        productIdInput.type = 'hidden';
        productIdInput.name = 'id';
        productIdInput.value = product.variants[0].id;
        form.appendChild(productIdInput);

        var quantityInput = document.createElement('input');
        quantityInput.type = 'hidden';
        quantityInput.id = 'quantity';
        quantityInput.name = 'quantity';
        quantityInput.value = '1';
        form.appendChild(quantityInput);

        var infoContainer = document.createElement('div');
        infoContainer.className = 'bs__infoContainer';
        infoContainer.style.display = 'flex';
        infoContainer.style.flexDirection = 'column';
        infoContainer.style.gap = '0.91rem';
        infoContainer.style.fontSize = '1.5rem';

        var name = document.createElement('a');
        name.href = product.url;
        name.style.color = 'black';
        name.innerText = product.title;
        name.style.fontSize = '1.7rem';
        name.style.textDecoration = 'none';
        infoContainer.appendChild(name);

        var price = document.createElement('div');
        price.classList.add('ProductMeta__Price');
        var productPriceInCents = product.price;
        var currencyCode = window.Shopify.currency.active;
        var userLocale = navigator.language || 'en-US';
        var formattedPrice = formatMoney(productPriceInCents, currencyCode, userLocale);
        var textNode = document.createTextNode(formattedPrice);
        price.appendChild(textNode);
        infoContainer.appendChild(price);

        var containerBottom = document.createElement('div');
        containerBottom.style.display = 'flex';
        containerBottom.style.gap = '0.5rem';

        var sizeContainer = document.createElement('div');
        sizeContainer.style.display = 'flex';
        sizeContainer.style.alignItems = 'center';
        sizeContainer.style.border = '1px solid #E1E1DA';

        var size = document.createElement('span');
        size.innerHTML = 'Size';
        size.style.padding = '6px';
        sizeContainer.appendChild(size);

        var select = document.createElement('select');
        select.className = 'bs__select';
        select.name = 'id';
        select.style.border = '0';
        select.style.maxWidth = '42px';
        select.style.backgroundColor = 'white';
        product.variants.forEach(function(variant) {
            var option = document.createElement('option');
            option.className = 'bs__option';
            option.style.fontSize = '10px';
            option.value = variant.id;
            option.textContent = variant.title;
            select.appendChild(option);
        });
        sizeContainer.appendChild(select);

        containerBottom.appendChild(sizeContainer);

        var submitButton = document.createElement('input');
        submitButton.type = 'submit';
        submitButton.className = 'bs__buttonClass';
        submitButton.style.background = 'black';
        submitButton.style.color = 'white';
        submitButton.style.border = 'none';
        submitButton.style.fontSize = '13px';
        submitButton.style.padding = '2px 27px';
        submitButton.value = 'Add';
        containerBottom.appendChild(submitButton);

        infoContainer.appendChild(containerBottom);
        form.appendChild(infoContainer);
        addToCartForm.appendChild(form);

        form.addEventListener('submit', function(event) {
            event.preventDefault();
            event.stopPropagation();
            submitButton.disabled = true;

            var formData = new FormData(form);
            var variantId = formData.get('id');
            var quantity = formData.get('quantity') || 1;

            var payload = {
                id: variantId,
                quantity: quantity,
                sections: cartDrawer.getSectionsToRender().map((section) => section.id),
                sections_url: window.location.pathname
            };
          
            fetch('/cart/add.js', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(payload)
            })
            .then(response => {
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                return response.json();
            })
            .then(data => {
                sendCartMessageToSlack();
                if (!window.location.search.includes('opencart=true')) {
               window.location = window.location.href + '?opencart=true';
               } else {
              var url = new URL(window.location.href);
              url.searchParams.delete('opencart');
              window.location = url.toString() + '?opencart=true';
              }

            })
            .catch(error => {
                console.error('Error:', error);
            })
            .finally(() => {
                submitButton.disabled = false;
            });
        });

    } else {
        console.error("Product or its variants not found.");
    }
}

//Function to format money
function formatMoney(cents, currency, locale = 'en') {
  if (isNaN(cents) || cents === null) {
    return '0.00';
  }
  const amount = cents / 100;
  return new Intl.NumberFormat(locale, { style: 'currency', currency: currency }).format(amount);
}

// Function to fetch a product by its handle
function fetchProduct(productHandle, elementId) {
    fetch(`/products/${productHandle}.js`)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(product => {
            createAddToCartForm(elementId, product);
        })
        .catch(error => {
            console.error('Error fetching product:', error);
        });
}

// Function to fetch cart items and determine recommendations
function fetchCartItems() {
    fetch('/cart.js', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        }
    })
    .then(response => response.json())
    .then(cartdata => {
        const keys = Object.keys(ProdCollections);
  
        const randomKeyIndex = Math.floor(Math.random() * keys.length); 
        const randomKey = keys[randomKeyIndex];
        const top = ProdCollections[randomKey].tops;
        const bottom = ProdCollections[randomKey].bottoms;
        let inCartBottoms = false;
        let inCartProds = false;
        let hardcodedtop = false;
        let hardcodedbottom = false;
        let currentabove = '';
        let currentbelow = '';

        cartdata.items.forEach(item => {
          for (const key in ProdCollections) {
                const collection = ProdCollections[key];
                 const above = collection.tops;
                const below = collection.bottoms;
        if (item.handle === above.handle) {
            inCartProds = true;
             currentabove = below.handle;
        }
        if (item.handle === below.handle) {
             inCartBottoms = true;
              currentbelow = above.handle;
        
        }
          }
          //replace this with your (best selling item as an alternaive) here and below in code where this dummy handel is given
        if(item.handle === 'canvas-lunch-bag') {
             hardcodedtop = true;
        }
         //replace this with your (best selling item as an alternaive) here and below in code where this dummy handel is given
        if (item.handle === 'camp-stool'){
             hardcodedbottom = true;
        }
    });


    if (inCartProds && inCartBottoms) {
         if (hardcodedtop && hardcodedbottom) {
            const prodreccontainer = document.getElementById("prod-recommend-container");
            // const prodHeading = document.getElementsByClassName("prod-recommend-heading");
            if (prodreccontainer) { 
              prodreccontainer.style.display = 'none';
            } else {
              console.error("Element with ID 'prod-recommend-container' not found");
            }
         }

         else if (hardcodedtop) {
                    fetchProduct('camp-stool', 'form2'); //replace here
         }
          else if (hardcodedbottom) 
         {
                    fetchProduct('canvas-lunch-bag', 'form1'); //replace here
         }
         else{
                fetchProduct('canvas-lunch-bag', 'form1'); //replace here
                fetchProduct('camp-stool', 'form2'); //replace here
         }
     
    }
    else if (inCartProds) {
         if (hardcodedtop && hardcodedbottom) {
               fetchProduct(currentabove, 'form2');
         } 
         else if (hardcodedtop) {
            fetchProduct('camp-stool', 'form2'); //replace here
            fetchProduct(currentabove, 'form1');
          }
          else {
          fetchProduct('canvas-lunch-bag', 'form2'); //replace here
          fetchProduct(currentabove, 'form1');
          }
     } else if (inCartBottoms) {
         if (hardcodedtop && hardcodedbottom) {
               fetchProduct(currentbelow, 'form1'); //replace here
         }
         else  if (hardcodedbottom) {
             fetchProduct(currentbelow, 'form1');
             fetchProduct('canvas-lunch-bag', 'form2');   //replace here
           }  
          else{
            fetchProduct(currentbelow, 'form1');
           fetchProduct('camp-stool', 'form2'); //replace here
          }
    } else {
          fetchProduct(top.handle, 'form1'); 
         fetchProduct(bottom.handle, 'form2');
   }

  
    })
    .catch(error => console.error('Error:', error));
}  

Follow the comments given in the code to replace it with your products

2.  Add the following code in cart-drawer.liquid snippet -

a) Paste this code below the scripts and before the starting of the custom element. 

<style>
  .drawer {
    visibility: hidden;
  }
  .prod-recommend-heading{
     display: flex; 
     justify-content: space-between;
   }

   .bs-arrowSlider{
     display: flex;
     align-items: center;
     gap: 0.5rem;
   }

   .fromSelector{
     display: flex;
     gap: 1rem;
     align-items: center;
   }
  
   #bs-scroll-div{
    display: flex;
    gap: 1rem;
    overflow: auto;
    height: 15rem;
  }

  #bs-scroll-div::-webkit-scrollbar {
  display: none;
 }
</style>

b) Paste this code where the custom element ends and the drawer footer starts 

 <div id="prod-recommend-container" style="background: #FFFFFF;">
        <div class="prod-recommend-heading">
         <h3 class="osc-heading">Best Sellers we Recommend</h3>
          <div class="bs-arrowSlider" style="display:flex; gap: 0.50rem;">
           <div id="bs-arrowSlider-left" style=" cursor: pointer;"> &#11013;</div>
           <div id="bs-arrowSlider-right" style=" cursor: pointer;">&#10145;</div>
           </div>
        </div>
         <div id="bs-scroll-div">
            <div class="bs-formContainer">  
             <div id="form1"></div>
            </div>
            <div class="bs-formContainer">                    
            <div id="form2"></div>
        </div>
         </div>
        </div>

c) Paste this code at the end of the file

<script>
  document.addEventListener("DOMContentLoaded", function() {
    const notifierslider = document.getElementById('bs-scroll-div');
    const notifierleftArrow = document.querySelector('#bs-arrowSlider-left');
    const notifierrightArrow = document.querySelector('#bs-arrowSlider-right');

    let notifierscrollAmount = 0;
    const notifierscrollStep = 260;

    notifierleftArrow.addEventListener('click', function() {
      notifierscrollAmount -= notifierscrollStep;
      notifierslider.scrollTo({
        top: 0,
        left: notifierscrollAmount,
        behavior: 'smooth'
      });
    });

    notifierrightArrow.addEventListener('click', function() {
      notifierscrollAmount += notifierscrollStep;
      notifierslider.scrollTo({
        top: 0,
        left: notifierscrollAmount,
        behavior: 'smooth'
      });
    });
  });
</script>

3.  Paste the following code in theme.liquid

Add it just before the body closing tag above </body>

 {{'cro-cart_recommendation.js' | asset_url | script_tag}}
     <script>
    if (window.location.href.includes('opencart=true')) {
        const cartDrawer = document.querySelector('cart-drawer');
        cartDrawer.classList.add('active');
    }
    </script>

and with this last step, you are all done with the setup.

Suggested Reads- How to Setup Sticky Add to Cart Feature in Shopify

Takeaway

It's a good strategy to use cart drawers to grab visitors' attention by showcasing products that can inspire additional purchases. By following the above given steps one can add the Product Recommendation Block which may help them to improve their conversion rates.

Conclusion

Adding recommended products to your Shopify cart page is a powerful strategy to increase sales and enhance the customer shopping experience. By following the steps outlined in this guide and continuously optimizing your approach, you can create a more engaging and profitable e-commerce store. Remember, the key to success lies in relevance, freshness, and continuous improvement of your product recommendations.

Expert Shopify Development Services

Implementing advanced features like dynamic product recommendations can significantly enhance your Shopify store's performance. However, it often requires a deep understanding of Shopify's architecture and best practices in e-commerce development. 

If you're looking to take your Shopify store to the next level with custom features and optimizations, consider working with experienced professionals. Hire Shopify experts who can not only implement these features flawlessly but also provide strategic insights to boost your online store's performance and conversion rates.

FAQs

1. Can we customize the cart page in Shopify?

Yes, Shopify allows extensive customization of the cart page. You can modify the layout, add features like product recommendations, and create a unique cart experience using Shopify's liquid templating system and custom JavaScript.

2. How do I add a product to my cart on Shopify?

To add a product to the cart programmatically, use Shopify's AJAX API. Send a POST request to '/cart/add.js' with the product variant ID and quantity. For standard add-to-cart buttons, use Shopify's built-in 'add to cart' form functionality.

3. Where is the cart setting in Shopify?

Access cart settings in your Shopify admin panel under "Settings" > "Checkout". Here you can configure various cart behaviours, including shipping options, customer information requirements, and abandoned checkout emails.