Shopify Product Grouping - dreamship-dev/dream GitHub Wiki

Shopify Product Grouping

Step-by-step guide to implement product grouping into any Shopify theme.


Overview

Product grouping effectively allows sellers to bypass the 100-variant limit by showing similar designed products on the same page in the same way a product option (color, size, etc.) is displayed. Grouped products can also be shown as thumbnail images.

The grouping is controlled using product tags in Shopify and can easily be edited in Shopify for any product.

Screenshot 2023-11-08 at 15 27 14

This guide assumes you have some knowledge of Theme development. This walkthrough uses the Dawn theme as a starting point. You may have slight differences in your code if you are using different themes.


Theme Setup

Step 1: Backup theme

Before starting any work its important that you create a duplicate of your existing theme to work on.

  1. From your Shopify admin, go to Online Store > Themes.
  2. For the theme that you want to duplicate, click Actions > Duplicate.

View Shopify docs for additional reference.

Step 2: Create collection.json.liquid file

  1. From your Shopify admin, go to Online Store > Themes.
  2. For the theme that you want to edit, click Actions > Edit Code.
  3. Under Templates, click Add a new template

Screenshot 2023-11-08 at 14 11 01

  1. When prompted, create a new template for collection, select liquid type and call it json. Screenshot 2023-11-08 at 14 11 16

  2. Select the newly created file collection.json.liquid and paste the following code:

{%- layout none -%}
{%- paginate collection.products by 50 -%}
  [
  {%- for product in collection.products -%}
    {%- if product.metafields.dreamship.product -%}
      {%- assign title = product.metafields.dreamship.product -%}
    {%- else -%}
      {%- assign title = product.title -%}
    {%- endif -%}
    {"id": {{- product.id -}},"title": "{{- title -}}","url": "{{- product.url -}}","thumbnail": "{{- product.featured_image | img_url: '160x160' -}}","tags": {{- product.tags | json -}}}{%-if product.id != collection.products.last.id -%},{%-endif-%}
  {%- endfor -%}
  ]
{%- endpaginate -%}
  1. Save the file

Step 3: Adding grouped product data onto the product page

  1. Inside of the code editor, open the main-product.liquid file in the Section folder. Note, if you are using older themes, then you might not have this file. Instead look for the product-template.liquid and continue the rest of the steps in that file instead.
  2. Paste the following code at the very bottom of the file:
{% comment %}
---------------- PRODUCT GROUPING  ----------------
{% endcomment %}
{% assign group_tags = product.tags | where: "group-" %}
{% assign has_group_tags = false %}
{% if group_tags.size > 0 %}
  {% assign has_group_tags = true %}
{% endif %}
{% for tag in group_tags %}
<script>
(function() {
    let requestUrl = "/collections/all/{{ tag }}?view=json";
    let request = new XMLHttpRequest();
    request.open("GET", requestUrl);
    request.onload = function() {
        if (request.status >= 200 && request.status < 300) {
            // Parse products JSON
            let products = JSON.parse(request.response);
          
            // Get the wrapper element
            let productGroupSelectWrapper = document.getElementById('ProductGroupSelectWrapper');

            // Show if items, else hide
            if (products.length > 1) {
              productGroupSelectWrapper.removeAttribute('hidden');
            } else {
              return
            }

            // Sort products A-Z
            products.sort(function(a, b){
              if(a.title < b.title) { return -1; }
              if(a.title > b.title) { return 1; }
              return 0;
            });
          
            // Get the select element
            let productGroupSelect = document.getElementById('ProductGroupSelect');

            // loop through each product
            products.forEach(function(product) {
                // Create a new option element
                let option = document.createElement("option");
                option.value = product.id;
                option.setAttribute("data-url", product.url);
                option.textContent = product.title;

                // Append the option to the select element
                productGroupSelect.appendChild(option);
            });

            // select functionality
            productGroupSelect.value = {{ product.id }};
            productGroupSelect.addEventListener('change', function() {
                let url = this.options[this.selectedIndex].getAttribute('data-url');
                location.replace(url);
            });
            
            // ADD ADDITIONAL CODE HERE
        }
    };
    request.send();
})();
</script>
{% endfor %}
{% comment %}
---------------- END PRODUCT GROUPING  ----------------
{% endcomment %}

Step 4: Using the grouped product data.

At this point in time, you now have grouped product data available to use on the page, we just need to inject it into different page elements. This part will vary depending on the theme you are using. For demonstration purposes, we are working off of the Dawn theme made by Shopify.

Product Dropdown

Follow this to add a select dropdown similar to other product options (size, color, etc.)

  1. Inside of the code editor, open the main-product.liquid file in the Sections folder.
  2. Find the product form. It probably looks something like this {% form 'product'....
  3. Add the following code inside of the form product tags.
{%- form 'product'-%}

<div hidden id="ProductGroupSelectWrapper" class="product-form__input product-form__input--dropdown">
  <label class="form__label">Product</label>
  <div class="select">
    <select class="select__select" id="ProductGroupSelect" data-name="product-type" data-index="option0"></select>
    {% render 'icon-caret' %}
  </div>
</div>

...EXISTING FORM CODE...

{% endform %}

If your product is using group tags, then you should see the new Product dropdown field.

Screenshot 2023-11-08 at 15 26 28

Note: You may need to edit the html structure or css class names to match your themes layout structure. It's important that you leave the ProductGroupSelectWrapper id attribute and the hidden attribute on the top most div element and the ProductGroupSelect id attribute on the select element.

OPTIONAL: Product Thumbnails

Follow this to add a product thumbnails underneath the details. This section requires you write your own css styles.

  1. Inside of the code editor, open the main-product.liquid file in the Sections folder.
  2. Find an area where you want your thumbnails to be. In this example we are putting it above the product description
  3. Add a new <div> element with the id attribute ProductGroupThumbnails. It should look something similar to below.
...

{%- when 'description' -%}
  <div id="ProductGroupThumbnails"></div>
  {%- if product.description != blank -%}
    <div class="product__description rte quick-add-hidden" {{ block.shopify_attributes }}>
      {{ product.description }}
    </div>
  {%- endif -%}

...
  1. Go back to JS code snippet at the bottom of the file and replace the code with the following:
{% comment %}
---------------- PRODUCT GROUPING  ----------------
{% endcomment %}
{% assign group_tags = product.tags | where: "group-" %}
{% assign has_group_tags = false %}
{% if group_tags.size > 0 %}
    {% assign has_group_tags = true %}
{% endif %}

<style>
  .ProductGroupThumbnailImage.active img {
    border: 1px solid black;
  }  
  .ProductGroupThumbnailImage img {
    border: 1px solid transparent;
    width: 80px;
    height: 80px;
  }
</style>

{% for tag in group_tags %}
    <script>
        (function () {
            let requestUrl = "/collections/all/{{ tag }}?view=json";
            let request = new XMLHttpRequest();
            request.open("GET", requestUrl);
            request.onload = function () {
                if (request.status >= 200 && request.status < 300) {
                    // Parse products JSON
                    let products = JSON.parse(request.response);

                    // Get the wrapper element
                    let productGroupSelectWrapper = document.getElementById('ProductGroupSelectWrapper');

                    // Show if items, else hide
                    if (products.length > 1) {
                        productGroupSelectWrapper.removeAttribute('hidden');
                    } else {
                        return
                    }

                    // Sort products A-Z
                    products.sort(function (a, b) {
                        if (a.title < b.title) {
                            return -1;
                        }
                        if (a.title > b.title) {
                            return 1;
                        }
                        return 0;
                    });

                    // Get the select element
                    let productGroupSelect = document.getElementById('ProductGroupSelect');

                    // Get the element where thumbnails will be appended
                    var productGroupThumbnails = document.getElementById('ProductGroupThumbnails');

                    // loop through each product
                    products.forEach(function (product) {
                        // Create a new option element
                        let option = document.createElement("option");
                        option.value = product.id;
                        option.setAttribute("data-url", product.url);
                        option.textContent = product.title;

                        // Append the option to the select element
                        productGroupSelect.appendChild(option);

                        // Create a new anchor element for the thumbnail
                        var anchor = document.createElement('a');
                        anchor.setAttribute('class', 'ProductGroupThumbnailImage');
                        anchor.setAttribute('href', product.url);

                        // Create the image element for the product thumbnail
                        var img = document.createElement('img');
                        img.setAttribute('src', product.thumbnail);
                        img.setAttribute('alt', product.title);

                        // Append the image to the anchor
                        anchor.appendChild(img);

                        // Optionally append a title or other information
                        // Uncomment the following lines if needed
                        // var titleSpan = document.createElement('span');
                        // titleSpan.textContent = product.title;
                        // anchor.appendChild(titleSpan);

                        // Append the anchor to the thumbnails container
                        productGroupThumbnails.appendChild(anchor);

                        // Check if the product id matches and add 'active' class if it does
                        if (product.id === {{ product.id }}) {
                            anchor.classList.add('active');
                        }
                    });

                    // select functionality
                    productGroupSelect.value = {{ product.id }};
                    productGroupSelect.addEventListener('change', function () {
                        let url = this.options[this.selectedIndex].getAttribute('data-url');
                        window.location.href = url;
                    });
                }
            };
            request.send();
        })();
    </script>
{% endfor %}
{% comment %}
---------------- END PRODUCT GROUPING  ----------------
{% endcomment %}

This will now add all of the thumbnails underneath your description that can be clicked. Screenshot 2023-11-08 at 15 49 50

  1. You now need to go in and clean up the styles for your thumbnails to match your theme. Note: the current product's image has an active css class applied to it that you can use to add selected borders, etc.

Step 5: Publish theme

Once you've tested that everything works, be sure to publish the new theme to your store.

  1. From your Shopify admin, go to Online Store > Themes.
  2. For the theme that you want to publish, click Actions > Publish.

Product Grouping

The grouping of products is controlled using product tags in Shopify and can be added to any product.

Note: Dreamship will automatically add group tags to products uploaded via the bulk uploader

Group tag naming

Tags must start with group- and look like group-TAG-NAME where TAG-NAME can be anything you choose.

Works: group-3902340209, group-tropical-designs, group-my_shirt_is_awesome

Doesn't work: GROUP-232332, groups-239402, group230403, group_320420

How it works

  1. Add the same group tag to every product you want to group together.
  2. You're done!

Advanced: Multiple tags

Group tags can be stacked, allowing you to build special product grouping if needed. For example, Product 2 will show Product 1 and Product 3 on its page, and show up on both of Product 1 and Product 2's page. However, Product 1 and Product 3 will never be on each other's pages.

Product Group
Product 1 group-a
Product 2 group-a, group-b
Product 3 group-b

Customization

Product Name

The current name used inside of the dropdown can be customzied. By default it is the title of the actual product, however, Dreamship typically adds metadata to the product on upload to created a cleaner .

Using your own metadata:

Inside of collection.json.liquid you will see the following code:

{%- if product.metafields.dreamship.product -%}
  {%- assign title = product.metafields.dreamship.product -%}
{%- else -%}
  {%- assign title = product.title -%}
{%- endif -%}

Replace it with:

{% if product.metafields.NAMESPACE.KEY %}
  {%- assign title = product.metafields.NAMESPACE.KEY -%}
{%- elsif product.metafields.dreamship.product -%}
  {%- assign title = product.metafields.dreamship.product -%}
{%- else -%}
  {%- assign title = product.title -%}
{%- endif -%}

Replace NAMESPACE and KEY to match the namespaces and keys you used for your metadata. Learn more about Shopify metafields

Always using the product title: If you don't want to use metafields, and only want to use the product title follow the steps below.

Inside of collection.json.liquid you will see the following code:

{%- if product.metafields.dreamship.product -%}
  {%- assign title = product.metafields.dreamship.product -%}
{%- else -%}
  {%- assign title = product.title -%}
{%- endif -%}

Replace it with:

{%- assign title = product.title -%}

Advanced: Additional product data

If you want additional product data to be sent over you can add it yourself easily in the collection.json.liquid file. This code essentially is generating json we can consume on the product page.

Inside of collection.json.liquid you will see the following code:

 {"id": {{- product.id -}},"title": "{{- title -}}","url": "{{- product.url -}}","thumbnail": "{{- product.featured_image | img_url: '160x160' -}}","tags": {{- product.tags | json -}}}{%-if product.id != collection.products.last.id -%},{%-endif-%}

You can add any product attribute to this. See the Shopify Product API docs for reference.

For example, if I wanted to add the vendor, I would replace the code above with:

{"id": {{- product.id -}}, "vendor": {{- product.vendor -}},"title": "{{- title -}}","url": "{{- product.url -}}","thumbnail": "{{- product.featured_image | img_url: '160x160' -}}","tags": {{- product.tags | json -}}}{%-if product.id != collection.products.last.id -%},{%-endif-%}

Note the "vendor": {{- product.vendor -}}, added after the id.


Limitations

Product Grouping Limits

The current product grouping code supports up to 50 grouped products.

Grouped Products Don't Load

In rare cases (<1%), the javascript used to load the grouped products will not execute on the page. More works needs to be done to determine the root cause and fix the issue. If you are able to successfully reproduce the error please reach out to [email protected].

⚠️ **GitHub.com Fallback** ⚠️