Skip to content

Product comparison

A Compare control on every product card collects pieces into a tray at the bottom of the screen. Open it and you get a matrix of up to four products side by side, built from what Shopify actually knows about them.

Theme settings → Quick view & wishlist

SettingTypeDefaultWhat it does
Enable product comparisonCheckboxOnThe compare control on cards, the tray, and the matrix dialog
  1. Compare appears in the top-left corner of each product card, opposite the wishlist heart.
  2. Tapping it adds the piece and raises a tray along the bottom of the window showing thumbnails of what’s collected.
  3. Tapping Compare (3) in the tray opens the matrix.
  4. Clear empties the selection; the × on each thumbnail removes one.

The selection is held by the shopper’s browser, so it survives moving between pages — they can collect three pieces across two collection pages and a search result, then open the matrix.

The tray is hidden entirely when nothing is selected, and the control reflects its own state with aria-pressed, so a piece already in the tray reads as selected.

Four is the maximum. Adding a fifth shows a toast reading “Compare up to 4 pieces at once” and the selection is left alone — the earliest pick isn’t silently dropped.

The cap and the message come from the same place, so the two can never disagree. If you translate compare.full, keep the {{ max }} variable in it.

Four is a layout limit, not an arbitrary one: five columns of product imagery on a phone-width matrix stops being comparable.

A header row of product images and names linking to each product, then one row per attribute:

RowSource
Priceproduct.price, formatted in the cart currency
Availabilityproduct.available → “In stock” / “Sold out”
Typeproduct.type
Made byproduct.vendor
(each option name)Every option any of the compared products has, with its values joined

Option rows are collected in first-seen order across all four products, so comparing a jacket with Size and Colour against a pack with Capacity gives you three option rows and the pack simply shows a dash under Size.

Two rules keep the matrix honest:

  • A row is only drawn when at least one compared product has a value for it. An attribute nobody has never becomes an empty band of dashes.
  • Shopify’s Title / Default Title pseudo-option is filtered out. Every single-variant product carries it; it’s internal plumbing, not a real choice, and rendering it produced a useless “Title: Default Title” row.

Missing values render as a dim em dash rather than an empty cell, so the grid stays readable.

Both the tray and the dialog are rendered empty in the page and filled by script from /products/{handle}.js. A shopper who never compares anything downloads nothing beyond that markup. Fetched products are cached for the life of the page, so opening and closing the matrix doesn’t re-request them.

The matrix is a real <dialog>, so the browser supplies the focus trap, the top layer and Escape-to-close. Opening it locks page scroll; closing releases it.

Grid columns are driven by a --cmp-cols custom property set to the number of products actually resolved, so a tray with one dead handle in it still lays out correctly.

Under compare in locales/en.default.json:

short (the card control), title, open, clear, add_label, remove, full, price, availability, type, vendor, in_stock, sold_out, view.

The row labels — price, availability, type, vendor — are read by the script from the layout’s translated string bundle, so translating them translates the matrix.