Sign in

Version history

Version history lets users browse earlier versions of a document and restore them, like in Google Docs or Figma. Each version is a snapshot of a room, capturing both its Storage and Yjs documents at a point in time. Versions are separate from multiplayer undo/redo, which tracks each user’s individual changes—a version instead records a meaningful milestone that anyone can return to later.

Step-by-step guide

This page is an overview for Storage, for a step-by-step setup guide, and to learn more about Yjs version history, read how to add version history to your app.

Creating versions

Versions can be created automatically at intervals, enabled in your project’s dashboard, or manually from your back end with Liveblocks.createVersionHistorySnapshot. Manual snapshots are useful at meaningful moments, for example:

  • When a user publishes, submits, or saves a document.
  • Before an AI agent makes a substantial change, so users can recover the previous state.
  • Before importing or migrating external data.
import { Liveblocks } from "@liveblocks/node";
const liveblocks = new Liveblocks({ secret: "",});
const { data } = await liveblocks.createVersionHistorySnapshot("my-room-id");
// { id: "vh_d75sF3..." }console.log(data);

Listing versions

On the client, useHistoryVersions returns the current room’s versions, newest first. Render them with the ready-made HistoryVersionSummary and HistoryVersionSummaryList components, which display each version’s authors and date.

import { useState } from "react";import { useHistoryVersions } from "@liveblocks/react/suspense";import {  HistoryVersionSummary,  HistoryVersionSummaryList,} from "@liveblocks/react-ui";
function VersionsSidebar() { const [selectedVersionId, setSelectedVersionId] = useState<string>(); const { versions, isLoading } = useHistoryVersions();
if (isLoading) { return <div>Loading versions…</div>; }
return ( <HistoryVersionSummaryList> {versions.map((version) => ( <HistoryVersionSummary key={version.id} version={version} selected={version.id === selectedVersionId} onClick={() => setSelectedVersionId(version.id)} /> ))} </HistoryVersionSummaryList> );}

These UI components are optional, but are a helpful starting point.

Previewing a version

useHistoryVersionStorageData returns the Storage data for a version, reconstructed as a read-only LiveObject. Use it to render a preview before the user decides to restore.

import { useHistoryVersionStorageData } from "@liveblocks/react/suspense";
function VersionPreview({ versionId }: { versionId: string }) { const { data, isLoading } = useHistoryVersionStorageData(versionId);
if (isLoading) { return <div>Loading version…</div>; }
// Render a read-only preview of the document at this version return <DocumentPreview document={data.toJSON()} />;}

Because an old version may not match your current Storage type, the data is typed as the more permissive LsonObject.

Restoring a version

useRestoreToStorageVersion returns a function that restores the room’s Storage to a given version.

import { useRestoreToStorageVersion } from "@liveblocks/react/suspense";
function RestoreButton({ versionId }: { versionId: string }) { const restore = useRestoreToStorageVersion(versionId);
return <button onClick={() => restore()}>Restore this version</button>;}

The restoration is applied as a single change and synchronized to everyone in the room. Because it’s added to the user’s undo history like any other edit, restoring is safe—a user who changes their mind can simply undo it.

Deleting versions

To allow users to remove a version, use useDeleteHistoryVersion. Unlike restoring, deletion is permanent and can’t be undone, so it’s best to ask for confirmation first.

import { useDeleteHistoryVersion } from "@liveblocks/react/suspense";
function DeleteButton({ versionId }: { versionId: string }) { const deleteHistoryVersion = useDeleteHistoryVersion();
return ( <button onClick={() => { if (confirm("Permanently delete this version?")) { deleteHistoryVersion(versionId); } }} > Delete </button> );}

Server-side access

Versions can also be listed from your back end with Liveblocks.getVersionHistory, which supports pagination, for example to build custom tooling or scheduled exports.

import { liveblocks } from "@liveblocks/node";
const liveblocks = new Liveblocks({ secret: "",});
const { data: versions, nextCursor } = await liveblocks.getVersionHistory("my-room-id");
// [{ id: "vh_d75sF3...", createdAt: ..., authors: [...] }, ...]console.log(versions);