# ProductGroup: The Schema for Product Variants

> What ProductGroup is in Schema.org, which properties Google asks for, how to mark it up on a single page or one page per variant, and how to check yours.

URL: https://clione.ai/glossary/productgroup/
Lang: en

A shirt sold in two colors and three sizes is six things you can buy and a single product idea: the shirt. In the markup, each of the six is a [Product](/glossary/product-schema/) and the idea that gathers them is a ProductGroup. It is a [Schema.org](/glossary/schema-org/) type, and its name is the vocabulary's own. **The ProductGroup is not sold: it is the parent that says what the variants have in common and how they differ.** It matters because it is how Google understands that six [product pages](/glossary/product-page/), or six options inside a single one, are the same item and not six products competing with each other. What counts as a variant in your catalog, and when a size or a pack is one, is explained in the entry on [product variants](/glossary/product-variants/); this entry covers the type that describes them in [structured data](/glossary/structured-data/).

## How it works

Schema.org defines ProductGroup as a group of products that vary only in well-described ways, such as size, color or material, and hangs it from Product: it is a kind of product. It adds a clarification that explains its design: **the group itself is not offered for sale, but the variants it represents are**. It works as a prototype or a template, and the properties set on the group represent characteristics shared by all its variants, except three that belong only to the group: `variesBy`, `hasVariant` and `url`.[^schema-org-productgroup]

It has three properties of its own. `hasVariant` points to each variant, and its inverse, `isVariantOf`, lets the variant point back to the group. `productGroupID` is a text identifier for the group. `variesBy` says which properties the variants vary on, for example size or color.[^schema-org-productgroup]

Google added its variants documentation on February 20, 2024, along with the `isVariantOf` property in the product documentation, and clarified then that it supports variants with different URLs.[^google-updates] According to that documentation, variant markup **makes your products eligible to show with their variant information** in [merchant listing](/glossary/merchant-listings/) experiences, and putting what is shared on the group, such as the brand or the reviews, cuts down on repeated information.[^google-product-variants] What it asks of the group:

| Property                                            | What Google says                                                                                                                                                    |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                                              | Required. The name of the group, such as "Wool coat", and on each variant a more specific one, such as "Wool coat, green, size S"                                   |
| `productGroupID`                                    | Recommended. The group's identifier, also called the parent [SKU](/glossary/sku/). If you also give it on each variant with `inProductGroupWithID`, it has to match |
| `variesBy`                                          | Recommended. The properties that tell the variants apart, with their full Schema.org URL: color, size, suggested age, suggested gender, material and pattern        |
| `hasVariant`                                        | Recommended. Each variant as a nested Product; or, the other way around, each variant points to the group with `isVariantOf`                                        |
| `brand`, `description`, `review`, `aggregateRating` | Recommended when they are the same for all variants. A brand set on the group does not need repeating on each one                                                   |
| `url`                                               | Only for single-page stores: the URL of the group with no variant selected                                                                                          |

Table based on Google's variants documentation.[^google-product-variants] Since May 20, 2026, that documentation also includes `hasAdultConsideration`, to mark products with adult content.[^google-updates]

Google distinguishes **two store designs**. In the single-page one, all variants are chosen without reloading, usually with URL parameters, and the group has a single [canonical](/glossary/canonical-url/) URL, the one with no variant preselected. In the multi-page one, each variant or each color has its own page; then no one URL represents the group, and **the full definition of the group is repeated on every page**, because it has to carry what is common to all. Each page carries the complete markup of its own variants and, for those on other pages, a variant that carries only `url`, so Google can find them.[^google-product-variants] In both designs, the variants can be nested inside the group or listed separately; Google recommends nesting them on a single page because it is the most compact and natural representation, and supports the others because some content management systems generate them more easily.[^google-product-variants]

And two technical conditions that decide whether the markup is any use. **Each variant needs a unique identifier**, with `sku` or `gtin` ([GTIN](/glossary/gtin/)), and each group needs its own. And the store has to be able to open each variant already selected from its own URL, with that variant's image, [price and availability](/glossary/price-and-availability/) and the option to add it to the cart, so Google can crawl and identify it.[^google-product-variants]

## In a store

**Fictitious example.** An invented store has one page per color of its linen shirt, and the size is chosen on each page with a parameter. This is the markup of the blue page, trimmed: the full group, its two blue variants and a variant with only the URL of the white page. The white page would repeat the same group with its white variants and a link back to the blue ones.

```json
{
  "@context": "https://schema.org",
  "@type": "ProductGroup",
  "name": "Calella linen shirt",
  "description": "Long-sleeve linen shirt, straight cut.",
  "brand": { "@type": "Brand", "name": "Calella" },
  "productGroupID": "CAL-LINEN",
  "variesBy": ["https://schema.org/color", "https://schema.org/size"],
  "hasVariant": [
    {
      "@type": "Product",
      "sku": "CAL-LINEN-BL-M",
      "name": "Calella linen shirt, blue, M",
      "color": "Blue",
      "size": "M",
      "offers": {
        "@type": "Offer",
        "url": "https://www.example.com/linen-shirt/blue?size=m",
        "price": "39.90",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      }
    },
    {
      "@type": "Product",
      "sku": "CAL-LINEN-BL-L",
      "name": "Calella linen shirt, blue, L",
      "color": "Blue",
      "size": "L",
      "offers": {
        "@type": "Offer",
        "url": "https://www.example.com/linen-shirt/blue?size=l",
        "price": "39.90",
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock"
      }
    },
    { "url": "https://www.example.com/linen-shirt/white?size=m" }
  ]
}
```

Three things to read in it. The group carries no `url`, because in a multi-page store it has none of its own. The brand and the general description go once, on the group, and hold for all the variants. And the price goes in each variant's offer, never on the group, because the group is not sold.


Putting the price and availability on the ProductGroup, as if it were the product being bought. Schema.org is explicit: the group is not offered for sale, its variants are what is sold.[^schema-org-productgroup] The offer goes inside each variant, with its price and its stock, and the group keeps what does not change.


## How to check it

1. **Open the source code of a page with variants** and look for `"@type": "ProductGroup"`. Check that it has `name`, `variesBy` and `productGroupID`, and that each variant has its own `sku` or `gtin` with no repeats.[^google-product-variants]
2. **If the variants have `inProductGroupWithID`**, check that it matches the group's `productGroupID`.[^google-product-variants]
3. **Open each variant's URL in a private window** and see whether the page loads with that variant selected: its image, its price, its availability and an active buy button.[^google-product-variants]
4. **If your store has one page per variant or per color**, open two and check that both carry the full group and a link to the other's variants.[^google-product-variants]
5. **Check which [rich results](/glossary/rich-results/) Google sees**: run the URL through the [Rich Results Test](/glossary/rich-results-test/) and see whether it finds the variants and with what errors or warnings.

## Related concept: product variants

Product variants are a catalog idea and ProductGroup is their description in the markup. **The variants live in your platform, with their options, inventory and codes; ProductGroup is how you tell a search engine about them.** The first is decided when the product is created, and the second has to reflect it without inventing anything: if the store sells nine combinations, the group links nine variants. The entry on [product variants](/glossary/product-variants/) explains how they are modeled on the platforms and in [Google Merchant Center](/glossary/google-merchant-center/), and where a variant ends and another product begins.

To go further, the [structured data reading path](/learn/structured-data/) orders the rest of the terms, and the [Offer](/glossary/offer-schema/) entry explains what each variant's offer carries.
