Skip to content
Neon UI
Esc
navigateopen⌘Jpreview
On this page

CheckpointTimeline

Checkpoint history with snapshot indicators and restore actions.

CheckpointTimeline is the workspace’s undo history: every agent checkpoint on a hairline rail, newest first, with square markers in the status vocabulary — the latest holds primary, the past stays muted. Each row carries the label, a relative timestamp, and mono metadata chips: the git sha, a snapshot indicator when a database snapshot rides along, and the tenant project id. Restore pins the acting row under a shimmering label and locks the rest until the flight lands. Before the first checkpoint exists, EmptyState holds the space.

  1. Added billing page with plan gating

    current2m ago

    f39ac2dsnapshotdamp-forest-123456

  2. Wired auth to session table

    18m ago

    b81e04asnapshotdamp-forest-123456

  3. Scaffolded CRM schema and seed data

    41m ago

    20d97c1damp-forest-123456

  4. Initial scaffold

    1h ago

    8a3f5e2snapshotdamp-forest-123456

Install

npx shadcn@latest add https://ui.neon.com/r/checkpoint-timeline.json

Usage

import { CheckpointTimeline } from "@/components/neon-ui/checkpoint-timeline";

<CheckpointTimeline
  checkpoints={checkpoints}
  restoringId={restoringId}
  onRestore={(id) => restoreCheckpoint(id)}
/>;

Wiring to Neon

A server component lists the project’s snapshots and wires restore as a server action:

import { createNeonClient } from "@/lib/neon-client";

const neon = createNeonClient(process.env.NEON_API_KEY);
const { data } = await neon.snapshots.list(projectId);

const checkpoints = (data ?? []).map((snapshot) => ({
  id: snapshot.id,
  label: snapshot.name,
  createdAt: snapshot.created_at,
  snapshot: snapshot.id,
  projectId,
}));

const restore = async (snapshotId: string) => {
  "use server";
  await neon.snapshots.restore(projectId, snapshotId);
};

Snapshots come back oldest first, so reverse them: the timeline expects newest first. restore creates a new branch rather than overwriting one. Pass targetBranchId to restore onto an existing branch instead, or a preview callback to check the restored data and commit or abort. Set waitForReadiness on the client and the call returns only once the restore has settled, so restoringId can clear on the await.

States

  • Entrance — rows rise 8px one at a time, 60ms apart, newest first.
  • Restoring — the acting row’s action swaps to a shimmering “Restoring…” and every other action dims until restoringId clears.
  • Empty — the built-in EmptyState explains what checkpoints are; replace it with the empty slot.

Props

PropType
checkpointsCheckpoint[]

Newest first: id, label, createdAt, plus optional sha, snapshot, projectId.

TypeCheckpoint[]
onRestore?(id: string) => void

Renders the restore action on every row.

Type(id: string) => void
restoringId?string | null

The checkpoint currently restoring; locks every other action.

Typestring | null
Defaultnull
empty?ReactNode

Override the built-in empty state.

TypeReactNode

Accessibility

  • The history is an ordered list — position in time is structural, not visual.
  • Restore buttons are real buttons with busy state communicated through the label swap; locked actions are disabled.
  • Markers and the rail are decorative (aria-hidden); the timestamp text carries recency.
  • Entrance stagger and shimmer are static under prefers-reduced-motion.