New structured_data filter for product schema markup - Product variants all with same @id

I am just trying out the new structured_data Liquid filter for product schema markup, and I am not quite sure if the output is correct. I will provide an example of the schema for products with multiple variants:

{
  "@id": "/products/example-product#parent",
  "@type": "ProductGroup",
  ...
  "hasVariant": [
    {
      "@id": "/products/example-product#product",
      "@type": "Product",
      "name": "Example Product - Variant 1",
      ...
    },
    {
      "@id": "/products/example-product#product",
      "@type": "Product",
      "name": "Example Product - Variant 2",
      ...
    },
    ...
  ]
  ...
}

I am not an expert with those markups, but from my understanding the @Id field of an entity is its unique identifier. In the output, this ID is the same for all the product variants. When I test the site with Google’s rich results test tool, it also seems like it only finds the first variant, because the entries are probably merged due to the equal IDs. Is this ID duplication a bug or is it intentional that all @Id fields are the same for some reason that I am not getting?

@VitaDev I have multiple tickets open with Shopify reporting this but they are not taking responsibility. The new structured data tags are included in the current version of Dawn 15.0.2 which is how I found out as it causes Google Search Console alerts. The id field either needs to be removed from the variants or it needs to be unique. I’m going to keep pushing on support and hopefully I can find someone who can get it resolved.

Hey Jesse, thanks for your reply, good to hear I am not the only one experiencing an issue here. That the filter is already in use in the latest Dawn version is pretty bad, one should probably switch back to an older version until this is fixed. I currently use custom code for the creation of the schema markup, so I can just wait until the filter actually works. But it would be nice to be able to shift the responsibility for valid and recent markup to the filter that is maintained by Shopify.

This is a confirmed bug in Shopify’s structured_data filter, not intentional behavior. You’re right that @id is supposed to be a unique identifier — when all variants share the same @id, Google’s parser treats them as the same entity and essentially merges them, which is why only the first variant shows up in the Rich Results Test.

The correct fix until Shopify patches the filter is to skip the structured_data filter entirely and write the variant schema manually in Liquid. The key is using the variant’s actual ID to generate a unique @id for each one:

"hasVariant": [
  {% for variant in product.variants %}
  {
    "@id": "{{ shop.url }}/products/{{ product.handle }}#variant-{{ variant.id }}",
    "@type": "Product",
    "name": {{ variant.title | json }},
    "sku": {{ variant.sku | json }},
    "offers": {
      "@type": "Offer",
      "price": "{{ variant.price | money_without_currency }}",
      "priceCurrency": {{ shop.currency | json }},
      "availability": "https://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}"
    }
  }{% unless forloop.last %},{% endunless %}
  {% endfor %}
]

This gives every variant a unique @id using its Shopify variant ID, so Google can distinguish between them correctly. Not ideal having to bypass the native filter but it works reliably until Shopify sorts this out.