ProductGroup schema connects variants by declaring a parent product with productGroupID, listing each variant through hasVariant, and naming the differing attribute in variesBy. On a single page, nest each Product under the ProductGroup. Across multiple pages, repeat the ProductGroup on every variant URL and link variants with isVariantOf or hasVariant, so Google can read one parent product with several child offers.
What does ProductGroup schema actually do?
ProductGroup is a structured data class for products sold in variations such as size, colour, material or pattern. It helps Google understand which products are variations of the same parent product, and it lets you specify common product-properties for all variants, such as brand and review information, which can reduce the duplication of information. Google's documentation describes ProductGroup as the main product and Product as each variant, with variesBy, hasVariant and productGroupID as the grouping properties (Official source).
Adding this markup also makes your products eligible for display with variant information in merchant listing experiences. It provides a clearer machine-readable relationship: one parent entity, several child entities, and an explicit statement of what differs between them. That matters for apparel, shoes, furniture, electronics and luggage, where the same model appears in many sizes or configurations.
Which properties connect variants, and what does each one mean?
| Property | Placed on | Purpose |
|---|---|---|
| productGroupID | ProductGroup | A stable identifier for the parent product, shared by all variants |
| hasVariant | ProductGroup | Points to each Product that belongs to the group |
| variesBy | ProductGroup | Names the attribute that changes between variants, such as size or colour |
| isVariantOf | Product | Points back to the parent ProductGroup |
| Product | Variant level | Carries the variant-specific offer, price, availability, SKU and URL |
Google's variant documentation lists variesBy, hasVariant and productGroupID as the properties used to group variants, alongside Product structured data (Official source). Treat productGroupID as the anchor: if it changes between pages that describe the same parent product, the grouping signal weakens.
Single page or multiple pages: which structure fits your site?
The choice is architectural, not stylistic.
Single page with nested variants. One URL shows the parent product and all variants, often through a selector. The ProductGroup sits at the top level and each variant Product is nested inside it using hasVariant. This suits sites where every size or colour is reachable on one page and the URL does not change when a shopper picks an option.
Single page with separate variant blocks. The variants are defined outside the ProductGroup block but still on the same page. This is useful when a template renders variant data in a different component. The relationship still needs hasVariant or isVariantOf to be explicit.
Multiple pages. Each variant has its own URL. The ProductGroup definition is repeated on every variant page, and each variant links to the parent through isVariantOf or is listed by the parent through hasVariant. Google's documentation notes that the full ProductGroup definition needs to be repeated on each of the variant pages (Official source).
A common failure is mixing models: nesting variants on one page while also publishing separate variant URLs with no parent reference. Decide per product type, then keep it consistent.
Example: a hypothetical multi-page variant setup
This example is illustrative only and uses invented values.
A retailer sells a jacket in three sizes, each on its own URL. On the page for size medium, the markup declares a ProductGroup with productGroupID "JK-100", variesBy "size", and a hasVariant entry for each of the three sizes. The medium Product carries its own SKU, price, availability and URL, plus isVariantOf pointing to the same ProductGroup. The small and large pages repeat the identical ProductGroup block and reference their own Product as the active variant.
The point is not the specific values. It is that the parent identifier stays constant while the variant-level offer data changes per URL.
How do I diagnose a broken variant setup?
Work through these checks in order, because each one depends on the previous layer being correct.
- Confirm the parent exists. Search the rendered HTML for productGroupID. If it appears only on one variant page, the group is incomplete.
- Check identifier consistency. Compare productGroupID across every variant URL for the same parent. Any mismatch breaks the grouping.
- Verify the variant link. Each Product should carry isVariantOf, or the ProductGroup should list it through hasVariant. One direction is enough if it is unambiguous.
- Confirm variesBy matches reality. If the page lets shoppers choose size and colour, the property should reflect what actually changes, not a generic label.
- Compare markup with the visible page. Price, availability and variant options in the markup must match what a shopper sees. This is the same discipline covered in Product schema: keep price and availability accurate.
- Test the rendered output. Use Google's Rich Results Test on a representative variant URL, then inspect a live URL to see what the crawler receives after JavaScript runs.
- Check the sitemap. Variant URLs that should be discoverable need to be listed, and the parent-to-variant relationship should be consistent with the markup.
If a step fails, fix that layer before moving on. Adding more properties to a broken parent-child relationship does not repair it.
What should stay on the parent and what belongs on the variant?
Shared attributes belong on the ProductGroup: brand, review information and other common product-properties for all variants. Variant-specific data belongs on each Product: SKU, price, availability, size, colour and the variant URL. This division reduces duplication and lowers the chance that one variant page contradicts another.
For shops where a single variant can carry more than one barcode, the identifier layer gets more complicated; that is discussed in Multiple Barcodes Per Shopify Variant: Implications for Product Feeds. Keep the barcode discussion separate from the grouping discussion, because they answer different questions.
What the evidence does not settle
Google's documentation describes the properties and the single-page and multi-page patterns. It does not promise that correct markup produces a variant display in merchant listings, and it does not publish a fixed threshold for how many variants a group should contain. Eligibility and display remain separate from correctness.
It is also not documented how every possible combination of nested and separate variant blocks is treated, or how a mismatch between productGroupID and a canonical URL is resolved. Where the documentation is silent, the safe approach is consistency: one parent identifier, one linking direction, and markup that matches the visible page. Verify behaviour on your own URLs rather than assuming a general rule.
Follow-up questions
Does every variant need its own URL for ProductGroup schema to work?
No. A single page with nested variants is a supported pattern. Separate URLs are a site architecture decision, not a requirement of the markup. What matters is that the parent-child relationship is declared clearly in whichever structure you use.
Can I use ProductGroup without adding Product markup to each variant?
ProductGroup describes the parent and its grouping properties, while Product structured data provides the variant details. Google's documentation presents ProductGroup alongside Product structured data, so the practical answer is to use both: ProductGroup for the relationship and Product for the variant details.
The short version
Use productGroupID to anchor the parent, hasVariant and isVariantOf to link children, and variesBy to state what changes. Choose one architecture per product type, repeat the parent definition on multi-page setups, and diagnose from the parent identifier outward. Correct markup improves how search engines interpret variants; it does not guarantee a particular display.