Almost everything we sell at RAMO comes in sizes. A repair connector comes in 1/2" and 3/4". A garden hose fitting comes in a few thread sizes. A switch frame comes in 2, 3 or 4 modules. For a while we treated every size as its own product, and it slowly made a mess of search, feeds and the catalog itself.

This is how we think about it now. Nothing fancy, just the rules we wish we'd written down on day one.

The question to ask first

Before deciding "one page or many", ask: would a customer ever search for this size on its own?

For plumbing, the answer is often yes. People type the exact size because the size is the whole point. Someone fixing a leak under the sink doesn't want "repair connector", they want the 1/2" one, today.

For things like colour on a switch frame, people usually search for the product and pick the colour after.

So we ended up with two patterns, not one.

Pattern 1: separate pages, one parent

When the size is something people search for, each size gets its own URL, its own title with the size in it, and its own stock and price. For example our 1/2" repair connector has its own page.

But they all point to a shared parent in the database:

create table product_groups ( id serial primary key, slug text unique not null, name text not null ); alter table products add column group_id int references product_groups(id), add column variant_label text; -- "1/2\"", "3/4\"", "3 modules" 

The product page reads its siblings by group_id and shows them as a size picker. Clicking a size navigates to the sibling's URL instead of swapping state on the page. It's a plain link, so crawlers follow it too.

Pattern 2: one page, a selector

When the variant is cosmetic, it's one page with a selector. The selected option goes in a query param (?color=white) so the link can be shared, and the canonical URL stays the bare product URL. We don't want five copies of the same page competing with each other.

Where it pays off: feeds

Google Merchant Center, Meta and TikTok all understand variant groups. In the feed, every size is its own item, and they share an item_group_id. Once the group lived in the database, the feed generator just reads group_id and writes it out. Before that, we were guessing groups from product names, which broke the first time someone renamed a product.

Things that bit us

  • Size in the title, written one way. 1/2", 1/2 inch, ½ and חצי צול all appeared in our titles at some point. Search treated them as different words. We picked one format for titles and put the other spellings into search synonyms.
  • Out-of-stock siblings. If the 3/4" is sold out, we still show it in the picker, greyed out, with a link. Hiding it made people think we never had it.
  • Structured data. Each size page has its own Product with its own offers. We don't put the whole group's price range on every page, since that confused the price shown in search results.
  • The category page. A pipe fittings category with every size listed separately gets long and repetitive fast. We show one card per group with a "more sizes" hint, and let filters split them out when someone filters by size.

The rule we keep

If the size changes what the customer needs, give it a URL. If it only changes what it looks like, give it a selector. Store the relationship in the database either way, because everything downstream (search, feeds, the picker, the category grid) wants to know which products belong together.

How do you handle variants in your store? I'm curious whether anyone has found a cleaner way to deal with the mixed "some variants deserve a page, some don't" case.