How To Implement ProductGroup Schema At Scale

eCommerce SEO

How To Implement ProductGroup Schema At Scale

Aaron Taylor

Aaron Taylor

30 Jun 2025

Key Takeaways

  • ProductGroup schema is an extremely efficient way to scale your product schema across a large product inventory
  • You need to link all your schema fields with one another via the same id
  • Utilise graph schema to match multiple types to your ProductGroup
  • Implement at scale via Google Tag Manager if you are unfamiliar with JavaScript or don’t have access to a developer
  • The potential drawback of this method is that all your product details that need to be dynamic will need to be available on the page so you can pull the CSS element of the page template
  • Once the schema is live then monitor it for errors with Google Search Console

What Is ProductGroup Schema?

ProductGroup schema is a schema type that allows you to mark up multiple variants of the same product under one schema field.

Around the end of last year Google announced Product Variant Structured Data (also known as ProductGroup schema) as a new way to mark up your products that have multiple versions of the same product.

This was a long time coming and it now allowed developers and SEOs to give structured data elements within a product group instead of treating products differently.

For example, if a brand sells a sweater in five different colours and three different sizes this would give fifteen different variants of the same product. So instead of treating them as 15 different products from a schema-perspective. We can now treat them as fifteen different variants of the same product group.

What Are The Different Types Of ProductGroup Schema?

There are two different types of ProductGroup schema:

  • Single page schemas
  • Multi page schemas

Single Page ProductGroup Schemas

Single page schemas are where a single page is recreated when toggling different options of the product.

A good example of a website that is appropriate for this schema type is Kmart’s eCommerce product detail pages (PDPs). The different product variants are recreated under the same URL whenever you change the different product elements on the page – e.g. sizes and colours.

Multi Page ProductGroup Schemas

A multipage ProductGroup schema is when a page refresh occurs between selection of the different product variants.

Potentially some URL re-writing is also delivered when a URL parameter is generated to specify the product colour or size.

What Are The Benefits Of ProductGroup Schema?

The benefits of ProductGroup schema is that it allows you to scale your implementation across all the different variants of your product catalogue, if implemented effectively.

This means that you can potentially increase the probability of ranking for thousands or millions of different niche, long-tail search terms depending on the size of your product catalogue.

In addition, it can help to match your schema more directly with your Google Merchant Centre feed and also rank better in Large Language Model (LLM) platforms like ChatGPT that are using product schema as a visibility factor in their organic shopping functionality.

ProductGroup Schema Example

That’s all great in theory, but how do we take action on any of this?

Great question! I have provided a completed a ProductGroup schema template, utilising the graph element, to also incorporate breadcrumb schema as an additional type below:

<script type="application/ld+json">

{

  "@context": "https://schema.org",

   "@graph": [

    {

      "@type": "ProductGroup",

      "@id": "{{Product Page URL}}#{{Product Name}}",

      "name": "{{Product Name}}",

      "description": "{{Product Description}}",

      "url": "{{Product Page URL}}",

      "brand": {

         "@type": "brand",

         "name": "{{Product Brand}}"

         },

      "manufacturer": "{{Product Manufacturer}}",

          "hasMerchantReturnPolicy": { 

            "@type": "MerchantReturnPolicy",

            "merchantReturnDays": "30",  

            "returnMethod": "ReturnInStore", 

            "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow"

          },

      "productGroupID": "{{Product Group ID}}",

      "variesBy": [

         "https://schema.org/size",

         "https://schema.org/color"

        ]

      },

      {

          "@type": "OfferShippingDetails",

             "@id": "{{Product Page URL}}#{{Product Name}}",

             "shippingRate": {

                "@type": "MonetaryAmount",

                "value": 0,

                "currency": "AUD"

                },

             "shippingDestination": {

                "@type": "DefinedRegion",

                "addressCountry": "AU"

                },

             "deliveryTime": {

                "@type": "ShippingDeliveryTime",

                "handlingTime": {

                   "@type": "QuantitativeValue",

                   "minValue": 1,

                   "maxValue": 3,

                   "unitCode": "DAY"

                   },

             "transitTime": {

                "@type": "QuantitativeValue",

                "minValue": 1,

                "maxValue": 20,

                "unitCode": "DAY"

                }

             }

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{Product Name #1}",

            "description": "{{Product Description #1}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #1}}",

                  "sku": "{{Product SKU #1}}",

                  "gtin": "{{Product gtin #1}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #1}}",

                  "image": "{{Product Image URL #1}}",

                  "price": {{Product Price #1}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #2}}",

            "description": "{{Product Description #2}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #2}}",

                  "sku": "{{Product SKU #2}}",

                  "gtin": "{{Product gtin #2}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #2}}",

                  "image": "{{Product Image URL #2}}",

                  "price": {{Product Price #2}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #3}}",

            "description": "{{Product Description #3}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #3}}",

                  "sku": "{{Product SKU #3}}",

                  "gtin": "{{Product gtin #3}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #3}}",

                  "image": "{{Product Image URL #3}}",

                  "price": {{Product Price #3}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #4}}",

            "description": "{{Product Description #4}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #4}}",

                  "sku": "{{Product SKU #4}}",

                  "gtin": "{{Product gtin #4}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #4}}",

                  "image": "{{Product Image URL #4}}",

                  "price": {{Product Price #4}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #5}}",

            "description": "{{Product Description #5}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #5}}",

                  "sku": "{{Product SKU #5}}",

                  "gtin": "{{Product gtin #5}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #5}}",

                  "image": "{{Product Image URL #5}}",

                  "price": {{Product Price #5}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #6}}",

            "description": "{{Product Description #6}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #6}}",

                  "sku": "{{Product SKU #6}}",

                  "gtin": "{{Product gtin #6}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #6}}",

                  "image": "{{Product Image URL #6}}",

                  "price": {{Product Price #6}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #7}}",

            "description": "{{Product Description #7}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #7}}",

                  "sku": "{{Product SKU #7}}",

                  "gtin": "{{Product gtin #7}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #7}}",

                  "image": "{{Product Image URL #7}}",

                  "price": {{Product Price #7}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },         

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #8}}",

            "description": "{{Product Description #8}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #8}}",

                  "sku": "{{Product SKU #8}}",

                  "gtin": "{{Product gtin #8}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #8}}",

                  "image": "{{Product Image URL #8}}",

                  "price": {{Product Price #8}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },         

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #9}}",

            "description": "{{Product Description #9}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #9}}",

                  "sku": "{{Product SKU #9}}",

                  "gtin": "{{Product gtin #9}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #9}}",

                  "image": "{{Product Image URL #9}}",

                  "price": {{Product Price #9}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },         

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #10}}",

            "description": "{Product Description #10}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #10}}",

                  "sku": "{{Product SKU #10}}",

                  "gtin": "{{Product gtin #10}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #10}}",

                  "image": "{{Product Image URL #10}}",

                  "price": {{Product Price #10}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #11}}",

            "description": "{{Product Description #11}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #11}}",

                  "sku": "{{Product SKU #11}}",

                  "gtin": "{{Product gtin #11}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #11}}",

                  "image": "{{Product Image URL #11}}",

                  "price": {{Product Price #11}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },              

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #12}}",

            "description": "{{Product Description #12}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #12}}",

                  "sku": "{{Product SKU #12}}",

                  "gtin": "{{Product gtin #12}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #12}}",

                  "image": "{{Product Image URL #12}}",

                  "price": {{Product Price #12}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },                  

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #13}}",

            "description": "{{Product Description #13}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #13}}",

                  "sku": "{{Product SKU #13}}",

                  "gtin": "{{Product gtin #13}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #13}}",

                  "image": "{{Product Image URL #13}}",

                  "price": {{Product Price #13}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },                  

         {

         "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #14}}",

            "description": "{{Product Description #14}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #14}}",

                  "sku": "{{Product SKU #14}}",

                  "gtin": "{{Product gtin #14}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #14}}",

                  "image": "{{Product Image URL #14}}",

                  "price": {{Product Price #14}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

         ]

      },

      {

      "@context": "https://schema.org",

         "@type": "Product",

            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },

            "name": "{{Product Name #15}}",

            "description": "{{Product Description #15}}",

            "offers": [

               {

                  "@type": "Offer",

                  "name": "{{Product Name #15}}",

                  "sku": "{{Product SKU #15}}",

                  "gtin": "{{Product gtin #15}}",

                  "availability": "https://schema.org/InStock",

                  "itemCondition": "https://schema.org/NewCondition",

                  "url": "{{Product URL #15}}",

                  "image": "{{Product Image URL #15}}",

                  "price": {{Product Price #15}},

                  "areaServed": "AU",

                  "priceCurrency": "AUD"

               }

            ]

         },          

         {

            "@type": "BreadcrumbList",

            "@id": "{{Product Page URL}}#{{Product Name}}",

            "itemListElement": [

         {

            "@type": "ListItem",

            "name": "{{Breadcrumb Anchor Text #1}}",

            "item": "{{Breadcrumb URL #1}}",

            "position": 1

         },

         {

            "@type": "ListItem",

            "name": "{{Breadcrumb Anchor Text #2}}",

            "item": "{{Breadcrumb URL #2}}",

            "position": 2

            }

         ]

      }

   ]

}

</script>

The  schema example above is based on a ProductGroup with fifteen different variants – five different colours and three different sizes.

I’ve used placeholder elements using the curly brackets – “{{ }}” so that you can easily apply your product details into JSON-LD.

ProductGroup Fields At The Group Level

This is where you add all your semantic and contextual information about your product group such as brand, manufacturer, return policies, and how your product varies – e.g. size, colour, etc.

By adding these fields to the ProductGroup we can easily apply all these overlapping fields to every product variant in your group using the id field – but more on that later.

{

      "@type": "ProductGroup",

      "@id": "{{Product Page URL}}#{{Product Name}}",

      "name": "{{Product Name}}",

      "description": "{{Product Description}}",

      "url": "{{Product Page URL}}",

      "brand": {

         "@type": "brand",

         "name": "{{Product Brand}}"

         },

      "manufacturer": "{{Product Manufacturer}}",

          "hasMerchantReturnPolicy": { 

            "@type": "MerchantReturnPolicy",

            "merchantReturnDays": "30",  

            "returnMethod": "ReturnInStore", 

            "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow"

          },

      "productGroupID": "{{Product Group ID}}",

      "variesBy": [

         "https://schema.org/size",

         "https://schema.org/color"

        ]

      },

Note: if your returns policy in your product schema differs to the information in your Google Merchant Centre this will send conflicting signals. So you want to make sure that both of these line up.

Using OfferShippingDetails At The Group Level

This schema type is again applied to the ProductGroup using the link of the common id.

{
          "@type": "OfferShippingDetails",
             "@id": "{{Product Page URL}}#{{Product Name}}",
             "shippingRate": {
                "@type": "MonetaryAmount",
                "value": 0,
                "currency": "AUD"
                },
             "shippingDestination": {
                "@type": "DefinedRegion",
                "addressCountry": "AU"
                },
             "deliveryTime": {
                "@type": "ShippingDeliveryTime",
                "handlingTime": {
                   "@type": "QuantitativeValue",
                   "minValue": 1,
                   "maxValue": 3,
                   "unitCode": "DAY"
                   },
             "transitTime": {
                "@type": "QuantitativeValue",
                "minValue": 1,
                "maxValue": 20,
                "unitCode": "DAY"
                }
             }
         },

Again, just like your returns policy – your shipping details also needs to line up with what your telling Google in Merchant Centre.

Utilising The isVariantOf At The Product Level

Now that we’ve made the key fields for the ProductGroup – returns policy, shipping details, and common product details – we can focus on how each product differs by product variant.

This is where we use the “isVariantOf” field for the Product schema. You can have unique SKUs, GTINs, pricing, images, and ratings (if you’re using them).

 {
         "@context": "https://schema.org",
         "@type": "Product",
            "isVariantOf": { "@id": "{{Product Page URL}}#{{Product Name}}" },
            "name": "{Product Name #1}",
            "description": "{{Product Description #1}}",
            "offers": [
               {
                  "@type": "Offer",
                  "name": "{{Product Name #1}}",
                  "sku": "{{Product SKU #1}}",
                  "gtin": "{{Product gtin #1}}",
                  "availability": "https://schema.org/InStock",
                  "itemCondition": "https://schema.org/NewCondition",
                  "url": "{{Product URL #1}}",
                  "image": "{{Product Image URL #1}}",
                  "price": {{Product Price #1}},
                  "areaServed": "AU",
                  "priceCurrency": "AUD"
               }
            ]
         },

Note: this is where we relay the information for each product in your product group back to the information in the previous ProductGroup schema. 

You use the same id to do this so that every product within your group is linked together via the matching id.

Including Breadcrumb Schema

Now you’ve linked all the information together via the above fields, breadcrumb schema is a common schema type to use on PDPs so I thought I’d include that here.

You can share the semantic relationship of how your ProductGroup fits within the wider product catalogue via the internal breadcrumb links.

For example, a purple sweater might look like:

Winter Clothes > Women’s > Sweaters

{

            "@type": "BreadcrumbList",

            "@id": "{{Product Page URL}}#{{Product Name}}",

            "itemListElement": [

         {

            "@type": "ListItem",

            "name": "{{Breadcrumb Anchor Text #1}}",

            "item": "{{Breadcrumb URL #1}}",

            "position": 1

         },

         {

            "@type": "ListItem",

            "name": "{{Breadcrumb Anchor Text #2}}",

            "item": "{{Breadcrumb URL #2}}",

            "position": 2

            }

         ]

      }

Note: you’re able to link this all together via the graph schema that was included at the beginning of the JSON-LD – allowing you to add multiple schema types to one semantic entity.

Linking It All Together With The Same ID

The way that the schema is structured is that every single element is linked by the same id – {{Product Page URL}}#{{Product Name}}.

{
      "@type": "ProductGroup",
      "@id": "{{Product Page URL}}#{{Product Name}}",

To make this unique I recommend that you use the product URL itself matched to the product name – as shown above.

Visualising ProductGroup Schema

If you’re still following along, prior to deploying your ProductGroup schema and tweaking the fields to your unique use case using the instructions above you’ll need to validate it and see how it visually represents your ProductGroup from an entity perspective.

Schema.org Validator

The first method I use to validate any schema is using schema.org’s validator.

If your schema is looking clean and ready to go as a template, that’s what you want before you work on actively deploying it in a live environment.

Classy Schema

If you want to see a deeper representation of how your schema fields overlap with one another I’d recommend Classy Schema’s visualisation function.

It represents the relationship between all the fields that you’ve built in your template to see the relationships between them all.

Implementing The ProductGroup Schema At Scale

The issue with making a schema of this static is that there are going to be dynamic elements that will need to be continually updated like availability and pricing.

If your inventory and pricing changes frequently I’d heavily recommend utilising Google Tag Manager so that your schema is matched to the product page level.

For example, if your pricing and availability is linked to a database or you manually change the information on the product page, this will be updated and reflected in your schema.

The good thing about matching it to the product page level is that if you’re using Google Merchant Centre Next you can update your feed via web scraping of the information of the product page as well.

Therefore, to optimise your Merchant Centre feed and your ProductGroup schema – all you need to do is update your PDP – or have that automatically linked to your inventory database.

Or if you’re familiar with JavaScript or have access to a developer then you can leverage their skills with Google’s JavaScript schema implementation documentation.

Utilising Google Tag Manager

If you’d like to pursue this method – definitely read this guide to implementing schema with Google Tag Manager.

Where you’ve created your dynamic elements you’ll simply create a new tag and input the CSS element from the PDP template.

See above I’ve copied the SKU information which could be input into a GTM trigger if I had access to Kmart’s GTM.

The drawback of this method is that you need to have all your elements on the product page itself so that you can dynamically pull it from Google Tag Manager.

Once the schema is live you can validate it via Google Search Console and monitor any errors that can be rectified.

Conclusion

If you need a way to scale your product schema I would highly recommend using ProductGroup schema.

Don’t be afraid of its complexity or if you don’t have a developer background. You can use third-party, non-technical friendly platforms like Google Tag Manager to handle the dynamic elements.

If you have any questions or need help with your SEO or GEO campaign then get in touch. And happy scheming!

Avatar photo

Aaron Taylor

Aaron Taylor is a Senior SEO Director for Prosperity Media, Australia’s leading specialist SEO agency based in Sydney. He’s worked on SEO campaigns for some of Australia and the world’s largest brands including multiple successful eCommerce strategies.