This July, we’ve added improvements to Storage and Comments.

- [Version history](#version-history): Snapshot and restore Liveblocks Storage
  documents.
- [LiveFile](#livefile): Upload and share files with our new data type for
  Liveblocks Storage.
- [Public and private threads](#private-threads): Set permissions on Comments
  threads.
- [AI Slideshow example](#ai-slideshow-generator): Generate slide decks with AI.

## Upgrade now [#upgrade-now]

To use the latest features, update your packages with the following command.

```bash
npx liveblocks upgrade
```

If you were previously on Liveblocks 3.17 or below, make sure to follow our
[upgrade guides](/docs/platform/upgrading) before updating.

## Version history for Liveblocks Storage [#version-history]

Liveblocks Storage now supports version history, enabling users to browse
previous snapshots of a room’s data, preview earlier states, and restore any
version when needed. You can easily create a version history dialog box in your
application, for example in this flowchart.

<Figure
  caption={
    <>
      Restoring an old history version in our{" "}
      <Link href="/examples/collaborative-flowchart-ai/nextjs-react-flow-ai">
        Collaborative Flowchart AI
      </Link>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="aLYxUslnASFz3Fz4Yr6Sk02ktTZnsLjU00OO8f01w5gaCc"
    alt="Restoring an old history version"
    static={true}
  />
</Figure>

### Creating version snapshots

Versions can be created automatically via a setting on the dashboard, or
manually on the server with
[`createVersionHistorySnapshot()`](/docs/api-reference/liveblocks-node#create-version-history-snapshot)—each
snapshot includes timestamps and contributor information.

```ts
const { data } = await liveblocks.createVersionHistorySnapshot("my-room-id");

// { id: "vh_d75sF3..." }
console.log(data);
```

### Listing versions

[`useHistoryVersions`](/docs/api-reference/liveblocks-react#useHistoryVersions)
returns a list of versions for a room, and our
[version history components](/docs/api-reference/liveblocks-react-ui#Version-history)
make it easy to display a polished version UI.

```tsx
import { useHistoryVersions } from "@liveblocks/react/suspense";

function VersionsList({ onSelectVersion }) {
  // +++
  const { versions } = useHistoryVersions();
  // +++

  return (
    // +++
    <HistoryVersionSummaryList>
      {versions?.map((version) => (
        <HistoryVersionSummary
          // +++
          onClick={() => {
            setSelectedVersionId(version.id);
          }}
          key={version.id}
          version={version}
          selected={version.id === selectedVersionId}
        />
      ))}
    </HistoryVersionSummaryList>
  );
}
```

### Previewing versions

Before restoring, you can preview or diff a version with
[`useHistoryVersionStorageData`](/docs/api-reference/liveblocks-react#useHistoryVersionStorageData),
which returns that version’s Storage as a read-only `LiveObject`.

```ts
import { useHistoryVersionStorageData } from "@liveblocks/react/suspense";

function StorageVersionPreview({ versionId }: { versionId: string }) {
  // +++
  const { data } = useHistoryVersionStorageData(versionId);
  // +++

  return (
    // +++
    <pre>{JSON.stringify(data.toJSON(), null, 2)}</pre>
    // +++
  );
}
```

### Restoring versions

With
[`useRestoreToStorageVersion`](/docs/api-reference/liveblocks-react#useRestoreToStorageVersion),
you can restore a version to the room’s Storage, applied as a single undoable
change.

```tsx
import { useRestoreToStorageVersion } from "@liveblocks/react";

function RestoreButton({ versionId }: { versionId: string }) {
  // +++
  const restoreToStorageVersion = useRestoreToStorageVersion(versionId);
  // +++

  return (
    // +++
    <button onClick={() => restoreToStorageVersion()}>
      {/* // +++ */}
      Restore this version
    </button>
  );
}
```

To learn more, read our guide on adding version history to your app.

<ButtonLink href="/blog/version-history-for-liveblocks-storage" size="lg">
  Learn more
</ButtonLink>

## LiveFile: upload and share files [#livefile]

We’ve released [`LiveFile`](/docs/api-reference/liveblocks-client#LiveFile), a
new data structure for storing files in a room’s Storage tree. Users can upload
files directly to their collaborative document, no third-party upload service
required, and everyone in the room will see them appear live.

<Figure
  caption={
    <>
      Uploading an image in our{" "}
      <Link href="/examples/tldraw-whiteboard/nextjs-tldraw-whiteboard-storage">
        Tldraw Whiteboard
      </Link>{" "}
      example.
    </>
  }
>
  <MuxVideo
    playbackId="6H2gch01700S8XzfcEu4O02ZsQsaTD2gJMCHcnZ3QSXxaw"
    alt="Uploading an image to a collaborative canvas with LiveFile"
    static={true}
  />
</Figure>

All file types are supported, including images, videos, and PDFs. To find code
snippets, and learn more, read the dedicated blog post on `LiveFile`.

<ButtonLink
  href="/blog/livefile-upload-and-share-files-on-collaborative-apps"
  size="lg"
>
  Learn more
</ButtonLink>

## Public and private threads [#private-threads]

You can now control who can see and reply to comment threads, enabling
internal-only notes and private review workflows. Comment threads now have a
`visibility` property—each thread is public by default, and can be made private
when created.

<Figure
  caption={
    <>
      Creating a private comment in our{" "}
      <Link href="/examples/comments-private/nextjs-comments-private">
        Private Commenting
      </Link>{" "}
      example.
    </>
  }
>
  <MuxVideo
    playbackId="MGkQwus2lk8crQNorXUd7P7r01iMFJ019VLsi5CI9qWnU"
    alt="Creating a private comment thread"
    static={true}
  />
</Figure>

To learn more, read our recent blog post on private comment threads.

<ButtonLink
  href="/blog/comments-private-threads-and-a-natural-home-for-ai"
  size="lg"
>
  Learn more
</ButtonLink>

## AI Slideshow Generator example [#ai-slideshow-generator]

We’ve added a new
[AI Slideshow Generator](/examples/ai-slideshow/nextjs-ai-slideshow) example,
highlighting a number of different ways you can use Liveblocks in your own
application.

<Figure>
  <MuxVideo
    playbackId="8WSSbFKqTwx101dV4duiWknpO2iZOFaaQ9Vuz00tIdnTk"
    alt="AI Slideshow Generator example"
    static={true}
  />
</Figure>

### Features

- **Generate slides**: Create slides with AI and approve/reject each
  modification.
- **Drag-and-drop**: Drag elements on each slide and double-click to edit text.
- **HTML-powered slides**: Edit the code behind each slide inside an editor.
- **Multiplayer**: Chat, document, and editor all use realtime multiplayer.
- **Commenting**: Pin draggable comments to each slide for user feedback.
- **Export**: Export slides as `.pptx` files, ready for presentation.

### How it works

Inside the example, chat messages are stored in
[feeds](/docs/collaboration-features/ai-collaboration#feeds), and update in
realtime for all users. Feeds APIs allow you to stream in answers, create new
chats, and list all existing chats within the UI.

Each slide is stored as HTML inside
[Yjs](/docs/collaboration-features/multiplayer/yjs), and a collaborative
[CodeMirror](/docs/get-started/yjs-codemirror-react) editor is used to edit the
HTML code. Drag-and-drop edits modify the HTML code behind each slide.

The comment pins use our
[default components](/docs/api-reference/liveblocks-react-ui#Thread) and
[hooks](/docs/api-reference/liveblocks-react#useThreads) for commenting. Each
thread holds x/y coordinates in thread metadata, which are used to position the
comment pin.

### Download it now

You can find the new AI slideshow example in our gallery.

<ButtonLink href="/examples/ai-slideshow/nextjs-ai-slideshow" size="lg">
  View the example
</ButtonLink>

## Minor improvements [#minor-improvements]

- Added a skill that can create examples, including images and code.
- Added `uploadAttachment()` to `@liveblocks/node` to upload comment
  attachments, combined with `attachmentIds` params in `createThread()`,
  `createComment()`, and `editComment()`.
- Added methods to upload comment attachments in the Python SDK, including
  multipart uploads.
- Added a button to download a room’s Liveblocks Storage as JSON from the
  dashboard’s room detail view.
- Added links to examples at the top of relevant get started guides, and links
  to guides in overview pages.
- Added more info on comments metadata throughout the docs.
- Added version history methods to `@liveblocks/node` and the Python SDK to list
  room versions, create a version snapshot, and delete a version.
- Added `useDeleteHistoryVersion()` to permanently delete a room version.
- New docs section on
  [rendering custom components](/docs/api-reference/liveblocks-react-ui#Rendering-custom-components)
  inside threads.
- Updated all pages in the pricing section, increasing clarity.
- Removed the `type` and `kind` fields from the `HistoryVersion` type in
  `@liveblocks/client`—the backend no longer returns these.
- Added `useHistoryVersionYjsData()` hook to retrieve raw Yjs binary data for a
  given version, deprecating `useHistoryVersionData()` in its favor.
- Prevented `Composer` in `@liveblocks/react-ui` from collapsing after focusing
  and blurring unless it was explicitly meant to support a collapsed state.
- Added a `visibility` prop to `Composer` in `@liveblocks/react-ui`.
- Updated the
  [Linear-like Issue Tracker](/examples/linear-like-issue-tracker/nextjs-linear-like-issue-tracker)
  example to use the latest Next.js caching features.

### Upgrade

To use these latest features, update your packages with:

```bash
npx liveblocks upgrade
```

## Contributors [#contributors]

<Contributors
  gitHubUsernames={[
    "ctnicholas",
    "marcbouchenoire",
    "nvie",
    "ofoucherot",
    "pierrelevaillant",
    "stacyschmitz",
  ]}
/>