Visorr for Craft CMS

Free · Pro $49, $29/year renewal

A list of dates is not a history

Craft stores a complete copy of your entry every time you save it — and then shows you four columns: number, date, author, notes. To find out what actually changed you open two revisions in two tabs and read. To put back the one paragraph somebody broke, you revert the whole entry and lose everything else that was fixed since. Visorr turns that list into something you can read, act on, and keep under control.

Visorr

Two commands, and your existing history is readable

There is nothing to backfill and no waiting for new saves. A revision in Craft is a complete duplicated element rather than a delta, so a comparison is "load two elements and walk one field layout" — which works just as well on history written years before Visorr was installed.

composer require justinholtweb/craft-visorr
php craft plugin/install visorr

{# Nothing needs to go in a template. The comparison screen and the sidebar
   panel are there as soon as it is installed. But if you want the history: #}
{% for revision in craft.visorr.history(entry, 10) %}
  {{ revision.label() }} — {{ revision.dateCreated|datetime }} by {{ revision.creatorName }}
{% endfor %}

{% set diff = craft.visorr.compare(entry, revisionId) %}
{{ diff.changedCount() }} fields changed

{# craft.visorr is read-only by design. Nothing on it deletes, restores or pins:
   a template rendering a page is not the place to change history. #}

Features

Everything Craft's revision screen leaves you to work out for yourself.

Compare any two versions

Field by field, with word-level highlighting — including a revision against what is currently live, which Craft cannot do at all. The screen opens on that comparison, because it is the one you almost always wanted.

  • Only the fields that moved, biggest change first
  • A forty-field entry with one edit is one row, not forty

Matrix blocks, aligned by content

A revision owns copies of its blocks with brand-new IDs, so there is nothing to match two revisions' blocks on. Visorr aligns them by content instead, in three passes.

  • An edit reads as changed, a reorder as moved
  • Without the third pass a rotation reads as delete plus insert

Restore one field, not the entry

Tick the fields you want and Visorr writes only those onto the live element. Everything you leave unticked stays exactly as it is — including the six things somebody else fixed this morning.

  • Previewed as what will be overwritten, before it writes
  • An ordinary save, so the restore is itself undoable

Retention as policy, not one number

Craft has a single global maxRevisions. Visorr resolves policy across element type, section and site, most specific first — and stores it in project config, so it deploys with the site and can be reviewed in a pull request.

  • A minKeep floor, so an untouched entry never empties out
  • No policy matching means Craft's own default, untouched

Pins that outlive every policy

Pin a revision and no prune will remove it — not Visorr's, and not Craft's own PruneRevisions job. Retention policy and editorial value are different things, and a tool that only understands the first will eventually delete the second.

  • Re-checked on every batch, so a late pin still wins
  • "Keep the last twenty" is storage; "keep the relaunch" is not

A preview that is the deletion

One resolver decides what gets pruned, and apply() deletes exactly the list it was handed — a preview that built its own query would be a preview of a different deletion. Every run is recorded with what it planned alongside what it did.

  • Drift is visible in the ledger rather than absorbed
  • A storage report showing which two sections are the problem

Pick any two versions

This is the screen, and these are real diffs — change either dropdown and the comparison recomputes. Note what it opens on: the last revision against what is live, which is the one comparison Craft cannot do at all.

Entries → About us → Visorr → Compare

Tick the fields you want to put back. Everything you leave unticked stays exactly as it is now.

Ticking a field and pressing Restore writes only that field onto the live entry. Because a restore is an ordinary save, it leaves a revision of its own — which is why a restore can itself be undone.

The hard part, made visible

A revision owns copies of its Matrix blocks with brand-new IDs, so there is nothing to match two revisions' blocks on. Flip the toggle to see what that costs, and what aligning on content instead buys you.

Entries → About us → Visorr → Compare → Body

The third alignment pass — a global reconciliation of identical leftovers — is the one that turns a rotation into “moved” rather than a deletion plus an insertion.

Policy, resolved in front of you

Craft has one global maxRevisions. Visorr resolves across three axes and takes the most specific match: element type beats section beats site. Change any of the three and watch which policy wins.

Settings → Plugins → Visorr → Retention
Most specific match wins

    Policies live in plugin settings and therefore in project config, so they deploy with the site and can be reviewed in a pull request. With no policy matching, Craft's own maxRevisions applies exactly as before.

    A preview that is the deletion

    One resolver decides what gets deleted and apply() deletes exactly the list it was handed — a preview that built its own query would be a preview of a different deletion. Change the scope, then apply it.

    Visorr → Prune
    Pins are re-checked on every batch

    Every run is recorded with the IDs it planned to delete alongside what it actually deleted. Apply “Everything” to see drift: four revisions were pinned mid-run, and the ledger says so rather than absorbing it.

    Frequently Asked Questions

    The questions worth answering before you install it.

    Read your history, then keep it honest

    Free for the comparison screen Craft should have shipped. $49 for policy across the whole site. Both work on the history you already have, from the moment you install it.