Version Scrubber

Lets people scrub through saved versions and see exactly what a restore would change before confirming it.

StopsRestored wrong version of document

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/version-scrubber.json

The files land in your project. They're yours to edit.

Usage

version-scrubber-usage.tsx
import { VersionScrubber, type Version } from "@/components/ui/version-scrubber"

return <VersionScrubber versions={versions} currentId={current.id} defaultSelectedId="b" onRestore={restore} className="max-w-md" />

What it catches

  • What a restore would change, as a word diff
  • Changed numbers, called out
  • Words lost and gained, counted in the confirmation
  • Selecting the current or an identical version (nothing to restore)
  • Restore failures, with the current version kept

Props

VersionScrubber props
PropTypeDefault
versions* (required)

Saved versions, oldest first.

Version[]—
currentId

The version that is live now. Defaults to the newest.

stringversions[versions.length - 1]?.id
selectedId

Controlled selected version.

string—
defaultSelectedId

Initially selected version when uncontrolled. Defaults to the current one.

string—
onSelectedIdChange

Called when the user scrubs to another version.

(id: string) => void—
onRestore* (required)

Make this version current. Return a promise to show progress; a rejection shows `errorText`.

(version: Version) => void | Promise<void>—
formatTime

Formats save times. Runs after mount, so server and client agree.

(date: Date) => stringdefaultFormat
confirmText

Restore confirmation. `{name}`, `{when}`, `{removed}`, `{added}` are filled in.

string"Restoring {name} from {when} removes {removed} and bring…
numbersText

Added to the confirmation when numbers differ; `{numbers}` is "14 → 30".

string"Numbers change: {numbers}."
keptText

Reassurance that restoring is not destructive.

string"The current text stays in the history."
heading

Other visible text and button labels.

string"Version history"
currentLabel

Badge for the current version.

string"Current"
backLabel

Label of the back-to-current button.

string"Back to current"
restoreLabel

Label of the Restore button.

string"Restore this version…"
confirmLabel

Label of the confirm-restore button.

string"Restore {name}"
cancelLabel

Label of the Cancel button.

string"Cancel"
isCurrentText

Shown when the selected version is current.

string"This is the current version."
identicalText

Shown when the selected version matches the current one.

string"Same text as the current version."
errorText

Shown when onRestore rejects.

string"Couldn't restore. Nothing was changed."

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Timeline slider is labelled with aria-valuetext naming the version
  • Diff parts have sr-only "comes back" / "removed" prefixes
  • Confirm is a labelled group; Cancel gets focus; Escape closes
  • Errors use role=alert; summary in a polite live region

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Word-level diff, not semantic
  • Restore runs through your onRestore

What it stops

Restored wrong version of document

Also known as

version history · restore version · diff · timeline · google docs history · revisions · time travel

Field type

Dependencies

Pairs well with