← Insights

Shopify product variants and SEO: when to split a product

How Shopify variant URLs, canonicals and ProductGroup markup work for search, and how to decide which options should be their own product.

7 min read

Contents · 7 sections

What is a Shopify variant to a search engine?

Shopify's variant documentation defines the unit: a product has up to three options, and one combination of option values is a variant, so size S in black is a variant of a t-shirt that comes in three sizes and three colours. A variant has no page of its own. It has an ID, and a product link that carries ?variant= plus that ID tells the theme which variant to preselect. The same documentation describes a second deep-link form, ?option_values= followed by one option value ID per option in option order, and says that if the combination does not exist, the values are selected but selected_variant comes back empty.

So one product page serves every variant, and what changes between them is the selected state: the media, the price and the selector. The documentation lists exactly those three as what a theme must update when a variant is linked.

For search, that gives two facts. There is one document per product in the index, not one per variant. And every variant URL is a near-copy of that document, which is why the canonical tag on a variant URL points to the flat /products/handle address.

Does Google want variants on separate URLs?

Google's guidance on URL structure for ecommerce sites says each variant should be identifiable by a separate URL, as a path segment such as /t-shirt/green or a query parameter such as /t-shirt?color=green. It adds that if the query parameter is optional, the URL with the parameter omitted should be the canonical.

Shopify's ?variant= parameter is that second pattern. A bare product URL is the canonical, each variant has an addressable URL, and Google can see the relationship between them. Nothing is broken by default.

The requirement hiding in the guidance is the other half. The variant URL has to work when Google requests it. Google's product variant structured data guidelines say the site must be able to preselect each variant with a distinct URL, and that preselecting includes the right image, price and availability, and the ability to add that variant to the cart. A theme that ignores the parameter on first load and only changes the selector with JavaScript afterwards fails that sentence.

What should the markup say about variants?

The same Google page describes the markup for a product with variants. A ProductGroup carries what the variants share, such as the name, description and brand. It names the options that differ in variesBy, gives the group an ID in productGroupID, and lists each variant as a Product under hasVariant, with its own sku or gtin and an offer whose url is the variant URL. Google says this nested form is its recommended one because it is the most compact way to represent a group and its variants.

Its technical guidelines add three rules that matter on Shopify:

  • every variant needs a unique ID in the markup, through sku or gtin, and every group needs a unique ID through productGroupID or inProductGroupWithID;
  • on a single-page site there must be one distinct canonical URL for the whole group, typically the base URL with no variant preselected;
  • Google recommends putting product markup in the initial HTML, and says that markup generated by JavaScript can make Shopping crawls less frequent and less reliable, which hurts for price and availability.

Shopify's structured_data filter is built to match. It outputs a schema.org Product when a product has no variants and a ProductGroup when it has one or more. If your theme prints that filter, the markup already follows the nested pattern, and the work is checking that an app or a theme edit has not replaced it. Our product schema guide covers what to check on the markup itself, and why the filter's output can be right while a second block from an app contradicts it.

When should a variant be its own product?

One canonical means one page in the index: one title, one first image, one description. A variant URL is folded into that page, so a variant can never rank for its own name. This is our reading of how the canonical works, not a Google statement, and it decides the question.

Ask what people type before they land on the product:

The option isExampleTreat it as
A fit decision made on the pageSize, length, pack countA variant. Nobody searches for "linen shirt size M" as a product.
A purchase detail with no search demand of its ownGift wrap, engravingA variant, or a line item property in the theme.
The thing a shopper names in the queryColour, scent, material, modelA candidate for its own product, with its own handle, title, images and description.
A different object that happens to share a templateA bundle against a single itemIts own product.

The test for the third row is Search Console, not taste. Open Performance, filter queries by the colour or model words you sell, and see which page Google shows. If one product page receives queries for several colours and the images shown are the first colour's, you have a page standing in for several searches, and each colour's own search is going unanswered.

Splitting has a cost. Each new product needs its own title, description and images, and its own inventory record. Do it for the options with demand, not across the catalogue.

How do you keep the shopper's view of a split product?

Shopify's combined listings exist for this, but they are available only on Shopify Plus. Each colour is a separate child product with its own images and URL handle, and a parent product ties them together so the shopper sees one product page with all the options. Shopify notes that the parent cannot be purchased, holds no inventory and has no sales data, and that the combination gives a total of six options instead of three.

Off Plus, you do the same by hand. Make one product per colour, give each its own handle and title, and link the colours to each other with swatches that are ordinary links to the other products. Each product keeps its own flat canonical, and ?variant= goes back to carrying only the size.

How do you check what your store does today?

Pick one product with several variants and run the following from a terminal, with $URL set to the product's address. This takes five minutes.

# 1. Which canonical does the bare URL write, and which does a variant URL write?
curl -s "$URL" | grep -o '<link rel="canonical"[^>]*>'
curl -s "$URL?variant=$VARIANT_ID" | grep -o '<link rel="canonical"[^>]*>'

# 2. Is the markup in the initial HTML, and does it describe a group?
curl -s "$URL" | grep -c 'ProductGroup'

$VARIANT_ID is any variant ID, which you can read from the product's .js address ($URL.js). The two canonical lines should be identical. A count of zero on the last line means the markup is either absent or injected by a script, which is the case Google warns about.

Then open the variant URL in a browser and check that the page loads with that variant selected, with its image, price and availability showing, and that the add-to-cart button adds that variant. That is the preselection Google asks for.

Finally, in Search Console, open Pages and look at the status "Alternate page with proper canonical tag". Variant and collection-scoped URLs belong there. If a product page you care about is listed under "Duplicate, Google chose different canonical than user", the canonical is not winning and the canonical guide explains what to look at.

What to fix first

  1. Run the check above on three representative products: one with a size option, one with a colour option, one with both (five minutes each).
  2. Fix any variant URL that does not preselect its variant, or any product page whose markup is missing from the initial HTML (an hour to a day, depending on whether the cause is the theme or an app).
  3. Pull the Search Console queries for your colour and model words and list the products where one page is receiving several searches (an hour).
  4. Split those products, starting with the ones with the most impressions, and keep sizes as variants (about an hour per product, plus redirects if you retire any old variant links).

If you would rather have the variant URLs, the markup and the split candidates read on your own store, that is part of what our Store Teardown looks at, and our services cover the catalogue work.

Next step

Own your growth.Start with the teardown.

Read it, then decide whether you want us to fix what it finds.