Version 5.0.1

October 5, 2026

Security

  • Comparisons could show elements a visitor shouldn't see. The table endpoint and the comparison page loaded ?ids= with status(null), adding resolved an ID with getElementById(), and stored lists did the same — and all of them are anonymous. Guessing IDs read the rows (prices, specs, titles) of disabled, scheduled and expired products and entries, and drafts; adding one returned its title and URL; and a share link kept showing a product after it was taken offline. Every path now loads elements through the element type's normal front-end query — live, no drafts — so anything else is left out of the table, refused when added, and drops out of the visitor's list, their session payload and any share link once it goes offline. Removing it still works. Staff viewing a visitor's comparison in the control panel see the same table the visitor does.

Fixed

  • An unknown set handle answered 500 instead of 400: the error message interpolated a variable called handle”.
  • A row type registered by another plugin that implements RowInterface directly, rather than extending BaseRow, took the whole table down — the table builder called BaseRow::resolveSafely() on every row. Its failures now leave an empty cell, as built-in rows' do.
  • The README and docs listed the JSON endpoints as compare/list/…; since the rebrand they are comparer/list/….

Changed

  • PHPStan and ECS configuration, and the findings they raised tidied.

Version 5.0.0

August 23, 2026

Initial release.

Added

  • Comparison sets, stored in project config: one element type each, any sections / category groups / product types, an item ceiling, and the rows of the comparison table.
  • Row types that resolve themselves off any element: title, image, custom field, element attribute, link, and — with Craft Commerce — price, SKU, stock and add-to-cart. Plugins can register their own through Rows::EVENT_REGISTER_ROW_TYPES.
  • Difference highlighting on normalized values rather than rendered markup, plus a "differences only" view (Pro).
  • Visitor compare lists held in the database: a guest's under a random cookie token, a signed-in user's under their account, with the guest list merged into the account on login.
  • A zero-dependency front-end runtime — compare buttons, a comparison bar with drag reordering, a modal, and a print stylesheet. No build step, no third-party requests.
  • A standalone comparison page at /compare, and shareable comparison links (Pro).
  • Insights (Pro): what visitors added, and which items they put next to each other.
  • A control-panel comparison browser, so support can see what a customer is looking at.
  • A documented JSON API, a Twig API, and compare / compare/prune console commands.
  • Lite and Pro editions. A lapsed Pro licence narrows the table rather than breaking the page.