Version List
A document's version history — time, author, name, kind badges (Current, Restored, Unsaved copy), a Named only switch and Load more — as a keyboard listbox.
- Status
- Since
0.23.74- Accessibility pattern
- listbox with one tab stop, labelled switch
Last updated
Version history
Install
Add Version List from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/version-listThe same command installs the registry items it composes: @vegastack/badge, @vegastack/button, @vegastack/person-avatar, @vegastack/relative-time, @vegastack/skeleton, @vegastack/switch, @vegastack/use-list-nav.
Usage
import { VersionList } from "@/components/ui/version-list";
<VersionList
versions={versions}
currentId={versions[0]?.id}
selectedId={selected}
onSelect={setSelected}
namedOnly={namedOnly}
onNamedOnlyChange={setNamedOnly}
loadMore={{ hasMore, onLoadMore: fetchMore, loading: fetching }}
/>;Each row shows when the version's text was saved and who wrote it; a named version leads with its
name. A restore is badged "Restored", a save that lost a race and was kept is "Unsaved copy", and
currentId is "Current". Pair it with DiffView for the selected
version.
Version history
Anatomy
Examples
Loading and empty
loading shows a skeleton; with no versions the list says "No versions yet" (emptyState).
Version history
Version history
No versions yet
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
versions* | readonly VersionItem[] | — | The versions, newest first. |
className | string | — | Classes for the list. |
currentId | string | null | The version that is the document's current text: a "Current" badge. |
emptyState | React.ReactNode | "No versions yet" | Shown when there are no versions. |
loading | boolean | false | Show the skeleton instead of the list. |
loadMore | { hasMore: boolean; onLoadMore: () => void; loading?: boolean; } | — | More versions exist: a "Load more" footer (keyset lists have no totals). |
namedOnly | boolean | false | Show named versions only (controlled). |
now | number | — | Pin the relative times' clock (docs, tests). |
onNamedOnlyChange | ((namedOnly: boolean) => void) | — | Called from the header's "Named only" switch; the switch shows when set. |
onSelect | ((id: string) => void) | — | Called when a version is picked — by click, or as the arrow keys move. |
selectedId | string | null | The selected version (shown in the diff beside the list). |
Data attributes and CSS variables on VersionList
| Attribute | Values |
|---|---|
data-kind | mirrors a prop or state value |
data-selected | "" |
data-slot | "version-list" | "version-list-empty" | "version-list-header" | "version-list-item" | "version-list-items" | "version-list-skeleton" |
| Prop | Type | Default | Description |
|---|---|---|---|
at* | string | number | Date | — | When that text was last saved. |
author* | Person | — | Who wrote the text this version holds. |
id* | string | — | Stable id. |
kind* | "auto" | "conflict" | "named" | "restore" | — | How it came to be: auto (an editing session), named, restore (a restore of an older
version — "Restored") or conflict (a save that lost a race, kept — "Unsaved copy"). |
name | string | — | A name the author gave it; named versions lead with it. |
summary | string | — | A muted second line — "12 words added". |
Accessibility
- The rows are a
listboxlabelled "Version history", each anoptionwitharia-selected; the list is one tab stop and the selection follows the arrow keys. - "Named only" is a
switchlabelled by its text.
| Key | Action |
|---|---|
| ↑ / ↓ | Select the previous / next version |
| Home / End | Select the newest / oldest shown |
| Tab | Leave the list |
| Contract | States tested |
|---|---|
| Behaviour | loading, empty, selected, named-only, loading-more |
| Accessibility | labeled, browser-accessibility-test, keyboard |
| Visual | default, selected |
Do / Don't
Comment Margin
Comment cards beside the text they are about — level with their highlight, stacked without overlap, the active one aligned — and a popover for narrow screens.
Diff View
What changed between two texts — changed lines, then the words inside them, struck through or tinted — lazily loaded, line-only for large pages.