ProductGroup Schema Generator

Turn a list of sizes and colors into ProductGroup markup with hasVariant, one Product and Offer per variant.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Product group

The one canonical URL all variants share.

Variants

Option values, then SKU, price and stock (in stock, out of stock, backorder or preorder). Optionally add a GTIN, then a Shopify variant ID or a full variant URL. Separate columns with |.

Store policies (optional)

Links each offer to your store-wide ShippingService. Build it with the shipping schema generator.

Links each offer to your store-wide MerchantReturnPolicy.

Paste this on the product page (replacing any existing Product markup), then test it in Google's Rich Results Test

product-group-schema.html
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "ProductGroup",
  "name": "Merino Crew Sweater",
  "description": "Midweight merino wool crew neck sweater. Machine washable.",
  "url": "https://example.com/products/merino-crew-sweater",
  "image": "https://example.com/images/merino-crew.jpg",
  "brand": {
    "@type": "Brand",
    "name": "Harbour & Co"
  },
  "productGroupID": "MCS-01",
  "variesBy": [
    "https://schema.org/size",
    "https://schema.org/color"
  ],
  "hasVariant": [
    {
      "@type": "Product",
      "sku": "MCS-S-NAV",
      "name": "Merino Crew Sweater - S / Navy",
      "size": "S",
      "color": "Navy",
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/products/merino-crew-sweater?size=s&color=navy",
        "price": 89,
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock",
        "itemCondition": "https://schema.org/NewCondition"
      }
    },
    {
      "@type": "Product",
      "sku": "MCS-M-NAV",
      "name": "Merino Crew Sweater - M / Navy",
      "size": "M",
      "color": "Navy",
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/products/merino-crew-sweater?size=m&color=navy",
        "price": 89,
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock",
        "itemCondition": "https://schema.org/NewCondition"
      }
    },
    {
      "@type": "Product",
      "sku": "MCS-L-NAV",
      "name": "Merino Crew Sweater - L / Navy",
      "size": "L",
      "color": "Navy",
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/products/merino-crew-sweater?size=l&color=navy",
        "price": 89,
        "priceCurrency": "USD",
        "availability": "https://schema.org/OutOfStock",
        "itemCondition": "https://schema.org/NewCondition"
      }
    },
    {
      "@type": "Product",
      "sku": "MCS-S-OAT",
      "name": "Merino Crew Sweater - S / Oatmeal",
      "size": "S",
      "color": "Oatmeal",
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/products/merino-crew-sweater?size=s&color=oatmeal",
        "price": 89,
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock",
        "itemCondition": "https://schema.org/NewCondition"
      }
    },
    {
      "@type": "Product",
      "sku": "MCS-M-OAT",
      "name": "Merino Crew Sweater - M / Oatmeal",
      "size": "M",
      "color": "Oatmeal",
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/products/merino-crew-sweater?size=m&color=oatmeal",
        "price": 94,
        "priceCurrency": "USD",
        "availability": "https://schema.org/InStock",
        "itemCondition": "https://schema.org/NewCondition"
      }
    }
  ]
}
</script>

5 variants in one ProductGroup. Every offer URL must open the page with that variant already selected, showing its price, image and stock.

Shopify themes and WooCommerce SEO plugins often print their own Product markup. Replace it rather than adding a second block, or Google sees two conflicting products.

How ProductGroup markup works

Google treats a product with sizes or colors as a ProductGroup. The group holds what every variant shares (name, brand, description, parent SKU) and lists each variant as a Product under hasVariant, with its own SKU, option values and offer.

  • variesBy names the options that change between variants. Google supports size, color, material, pattern, suggestedAge and suggestedGender. This tool covers all except suggestedAge, which needs an age range rather than text.
  • productGroupID is your parent SKU. It must be unique.
  • Each variant needs a unique ID (SKU or GTIN) and its own URL that opens the page with that variant already selected.
  • url on the ProductGroup is only for single-page setups, where every variant lives on one product page. That's how Shopify and WooCommerce work by default.

The tool builds each offer URL for you: ?size=m&color=navy style parameters, WooCommerce's ?attribute_pa_size=m parameters, or Shopify's ?variant= ID from the last column.

Adding it to Shopify or WooCommerce

  1. 01

    Check for existing markup

    View the product page source and search for "@type": "Product". Most themes and SEO plugins already print Product markup. Replace it rather than adding a second, conflicting block.

  2. 02

    Shopify

    Go to Online Store → Themes, open the … menu and choose Edit code. The markup usually lives in the product section or a snippet. For a whole catalog, turn this output into Liquid that loops over product.variants.

  3. 03

    WooCommerce

    Use your SEO plugin's schema settings if it supports ProductGroup, or print the JSON-LD in wp_head for that product with a code snippets plugin. Variation URLs use attribute_pa_{slug} for global attributes, so check the slugs under Products → Attributes.

  4. 04

    Test it

    Run the page through Google's Rich Results Test and check the Merchant listings report in Search Console after Google recrawls.

Optional: link each offer to your store-wide policies with the return policy and shipping generators. The offers then reference them by @id.

Common mistakes

  • Variant URLs that don't preselect anything. Google needs each URL to show that variant's price, image and stock, and to let shoppers add it to the cart.
  • Duplicate SKUs. Two variants with the same SKU look like one product to Google. The tool warns you.
  • Markup that changes as shoppers click. Google says the markup on a single-page site shouldn't change when a different variant is picked. Print all variants in the initial HTML.

Questions people ask

Do I need ProductGroup if each variant is a separate product page?

Yes, but the markup differs. On multi-page setups, each page repeats the full ProductGroup and you leave out the group's url. This tool builds the single-page version, which is what Shopify and WooCommerce use.

Can I include GTINs?

Yes. Add the barcode as the column after the stock status. The tool accepts 8, 12, 13 or 14 digits and leaves out anything else with a warning.

What's the difference between productGroupID and inProductGroupWithID?

They carry the same parent SKU. Put productGroupID on the ProductGroup, or inProductGroupWithID on each variant. If you use both, they must match. This tool uses productGroupID.

Will this give me rich results?

It makes the page eligible for Google's merchant listing and product variant features. Google decides when to show them.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com