Today, we’re introducing [Liveblocks Sync][], our sync engine for the agentic
web. Using Sync, you can build realtime multiplayer applications where humans,
agents, or both, can edit documents at the same time.

<SyncHeroIllustration />

Sync was previously known as Liveblocks Storage, but we’ve expanded its scope to
include features for the modern web, such as
[LiveText, our new collaborative text editing technology](/blog/livetext-a-new-primitive-for-collaborative-text-editing),
an alternative to the popular Yjs library.

## Who needs Liveblocks Sync?

Sync is for anyone building an app around documents or artifacts, such as text
editors, canvases, spreadsheets, flowcharts, AI workspaces. It’s especially
useful if you don’t have a dedicated team of infrastructure engineers, or you’d
rather not spend months building realtime collaboration from scratch.

<Figure caption={<>Apps powered by Liveblocks Sync</>}>
  <MuxVideo
    playbackId="024itAR2pSv2U02IEZHchZ8QX2cWjgBMWwxxT006POq800s"
    alt="Example of Sync in action"
    static={true}
  />
</Figure>

## Why do you need it?

When people and agents edit a document at the same time, the last write usually
wins, and everyone else loses their work. WebSockets can stream changes, but a
live connection is still not a consistent document, and leaves you with issues:

- **Conflicting edits**: Users lose data when editing at the same time.
- **Network latency**: Every change needs to wait for a remote confirmation.
- **Reconnection**: Poor networks risk losing user data when reconnecting.
- **Persistence**: State is lost when the session ends or the page reloads.

### Sync enables multiplayer

[Liveblocks Sync][] is a sync engine that integrates into your app, enabling
multiplayer experiences where humans, agents, and devices work on the same
document together. When using it, you update a shared document as if it were
local state, and the engine solves each of those problems for you:

- Liveblocks acts as the source of truth, safely merging simultaneous edits.
- Local changes apply immediately, before syncing in the background.
- When a user disconnects, offline edits queue locally and merge on reconnect.
- Documents automatically save to our scalable, persistent storage.

## How Sync works

[Liveblocks Sync][] supports any product experience where people, agents, or
devices share live state—from canvases, to flowcharts, text editors, and more.

<SyncUseCases />

Using each of Sync’s multiplayer integrations and primitives, such as Storage,
you can make a new or existing application multiplayer.

### Storage

[Storage][] is a
[CRDT-like](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)
primitive, inspired by Figma’s multiplayer technology, that allows you to read
and write to
[conflict-resolved](/docs/guides/how-conflict-resolution-works-in-liveblocks-sync)
collaborative state, allowing you to build true multiplayer documents. For
example, in the video below, Storage is used to store slides and their content.

<Figure
  caption={
    <>
      Multiplayer editing in the{" "}
      <Link href="/examples/ai-slideshow/nextjs-ai-slideshow">
        AI Slideshow
      </Link>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="4uuDhQ3pLNkoKl9s2BhD301N9Jr01B6BDwcLY1B7ljiOc"
    alt="Example of a collaborative AI slideshow editor"
    static={true}
    height={520}
    width={768}
  />
</Figure>

Under the hood, Storage uses
[conflict-free data types](/docs/products/sync/storage#Conflict-free-data-types)
to read and write to collaborative state. These data types work similarly to
JavaScript structures, such as objects, arrays, and strings, except they sync
their data in realtime, merging changes automatically.

```ts
import { LiveObject, LiveList, LiveText } from "@liveblocks/client";

// Create a list of slides
// +++
const slides = new LiveList([]);
// +++

// Create a new slide
// +++
const newSlide = new LiveObject({
  title: "Untitled",
  content: new LiveText(["<h1>Welcome!</h1>"]),
});
// +++

// Add the new slide to the list=
// +++
slides.push(newSlide);
// +++
```

In React, state can be read and modified using the [`useStorage`][] and
[`useMutation`][] hooks respectively. For example, you can fetch a list of every
slide, create a callback to add new ones, and add these to your app’s UI.

```tsx
import { useStorage, useMutation } from "@liveblocks/react/suspense";

function Slides() {
  // Fetch realtime data, e.g. an array of slides
  // +++
  const slides = useStorage((root) => root.slides);
  // +++

  // Update realtime data, e.g. add a new slide to the array
  // +++
  const addSlide = useMutation(({ storage }) => {
    const newSlide = new LiveObject({
      title: "Untitled",
      content: new LiveText(["<h1>Welcome!</h1>"]),
    });

    storage.get("slides").push(newSlide);
  }, []);
  // +++

  return (
    <div>
      // +++
      {slides.map((slide) => (
        <Slide key={slide.id} slide={slide} />
      ))}
      <button onClick={addSlide}>➕ Add Slide</button>
      // +++
    </div>
  );
}
```

These hooks update in realtime as other users edit the document, and your app
re-renders accordingly. Learn more about [Storage](/docs/products/sync/storage).

### Presence

[Presence][] is another primitive, it allows you to show realtime user activity
in your application, such as live cursors or an avatar stack. In the video
below, a user’s live cursor is shown, and their avatar appears when they edit an
item.

<Figure
  caption={
    <>
      Live cursors and avatars in the{" "}
      <UniversalLink href="/nextjs-starter-kit">
        Next.js Starter Kit
      </UniversalLink>
    </>
  }
>
  <MuxVideo
    playbackId="IQi7wClOlXt1GoMDMB02uDbkPyOYSe46xb01w9K33XNwU"
    alt="Presence"
    static={true}
  />
</Figure>

With the [`useOthers`][] React hook, you can retrieve an array of every other
connected user, and render presence how you like. For example, to show an avatar
for each connected user, you can use the following code:

```tsx
import { useSelf, useOthers } from "@liveblocks/react/suspense";

function AvatarStack() {
  // Get an array of every other connected user
  // +++
  const others = useOthers();
  // +++

  // Render an avatar stack
  return (
    <div>
      // +++
      {others.map(({ connectionId, info }) => (
        <img key={connectionId} src={info.avatar} alt={info.name} />
      ))}
      // +++
    </div>
  );
}
```

For more complex presence, add [`useUpdateMyPresence`][] to share JSON presence
values with other users, such as whether a user has selected an input.

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

function Input({ inputId }: { inputId: string }) {
  // +++
  const updateMyPresence = useUpdateMyPresence();
  // +++

  return (
    <div>
      // +++
      <input
        // Update the user's selected input in presence
        onFocus={() => updateMyPresence({ selectedId: inputId })}
        onBlur={() => updateMyPresence({ selectedId: null })}
      />
      // +++
    </div>
  );
}
```

To show which users are selecting the input, add [`useOthers`][], filter for
users with the same `selectedId`, and render their avatars.

```tsx
import { useOthers, useUpdateMyPresence } from "@liveblocks/react/suspense";

function Input({ inputId }: { inputId: string }) {
  const updateMyPresence = useUpdateMyPresence();

  // Users that are selecting this input
  // +++
  const others = useOthers();
  const selectedOthers = others.filter(
    ({ presence }) => presence.selectedId === inputId
  );
  // +++

  return (
    <div>
      <input
        onFocus={() => updateMyPresence({ selectedId: inputId })}
        onBlur={() => updateMyPresence({ selectedId: null })}
      />
      // +++
      {selectedOthers.map(({ connectionId, info }) => (
        <img key={connectionId} src={info.avatar} alt={info.name} />
      ))}
      // +++
    </div>
  );
}
```

Learn more about [Presence](/docs/products/sync/presence).

### Feeds

[Feeds][] is a primitive that allows you to create realtime paginated streams of
data. Use it to build AI chats with memory, display live agent activity, and
give users a complete history of conversations and completed work. You can
stream messages from both the client or server and display them live in your
application.

<Figure
  caption={
    <>
      Multiplayer chat in the{" "}
      <UniversalLink href="/examples/ai-slideshow/nextjs-ai-slideshow">
        AI Slideshow Generator
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="eqjtSmRtWhvnX00hNyqnb5dU2O52SsMiH7Y6u013THiCI"
    alt="Multiplayer chat"
    static={true}
    height={520}
    width={768}
  />
</Figure>

To set this up, stream messages into your UI with [`useFeedMessages`][], and
create new messages with [`useCreateFeedMessage`][]. With these hooks, you can
create a chat interface like the one in the video above, using a UI library of
your choice.

```tsx
import {
  useFeedMessages,
  useCreateFeedMessage,
} from "@liveblocks/react/suspense";
import { generateResponse } from "./generate-response";

function Chat({ feedId }: { feedId: string }) {
  // +++
  const { messages } = useFeedMessages(feedId);
  const createFeedMessage = useCreateFeedMessage();
  // +++

  return (
    <>
      <Conversation>
        // +++
        {messages.map((message) => (
          <Message key={message.id} from={message.data.role}>
            <MessageResponse>{message.data.content}</MessageResponse>
          </Message>
        ))}
        // +++
      </Conversation>
      <PromptInput
        onSubmit={(message) => {
          // +++
          createFeedMessage(feedId, {
            role: "user",
            content: message,
          });
          generateResponse(roomId, feedId, messages);
          // +++
        }}
      >
        <PromptInputTextArea />
      </PromptInput>
    </>
  );
}
```

To render a generated response from the AI, use [`createFeedMessage`][] to
create a new message, then use [`updateFeedMessage`][] to stream in each chunk
of the AI’s response.

```tsx title="generate-response.ts"
"use server";

export async function generateResponse(roomId, feedId, messages) {
  const messageId = crypto.randomUUID();

  // +++
  await liveblocks.createFeedMessage({
    roomId,
    feedId,
    messageId,
    data: { role: "assistant", content: text },
  });
  // +++

  const { textStream } = streamText({
    model: "openai/gpt-5.6-sol",
    system: "You are a helpful assistant in a team chat.",
    messages,
  });

  let content = "";

  for await (const chunk of textStream) {
    content += chunk;
    // +++
    await liveblocks.updateFeedMessage({
      roomId,
      feedId,
      messageId,
      data: { role: "assistant", content },
    });
    // +++
  }
}
```

Learn more about [Feeds](/docs/products/sync/feeds).

### Text editing

Sync allows you to add fully-featured collaborative text editing to your editor,
enabling a multiplayer experience like Google Docs. Start with one of our
ready-made integrations for editors like Tiptap, BlockNote, CodeMirror, or build
from scratch with [Yjs][] or LiveText,
[our new collaborative text editing technology](/blog/livetext-a-new-primitive-for-collaborative-text-editing).

<Figure
  caption={
    <>
      Text editing inside the{" "}
      <UniversalLink href="/nextjs-starter-kit">
        Next.js Starter Kit
      </UniversalLink>
    </>
  }
>
  <MuxVideo
    playbackId="ONcgHFhTTFJ7VcqgK8Ai4fmk4ATu02HLi3w5u3bknxi4"
    alt="Text editor"
    static={true}
    height={520}
    width={768}
  />
</Figure>

Each editor requires different setup code, but they’re all similarly easy to
integrate. Plus, extend your own editor with any extensions or plugins, and Sync
will ensure they’re multiplayer too, for example with
[our Tiptap integration](/docs/products/sync/text-editing/tiptap).

```tsx
import { useLiveblocksExtension } from "@liveblocks/react-tiptap";
import { useEditor, EditorContent } from "@tiptap/react";

function TextEditor() {
  // +++
  const liveblocks = useLiveblocksExtension();
  // +++

  const editor = useEditor({
    extensions: [
      // +++
      liveblocks,
      // +++

      // Other extensions
      // ...
    ],
  });

  return <EditorContent editor={editor} />;
}
```

Learn more about [Text editing](/docs/products/sync/text-editing).

### Agentic editing

Sync’s APIs were designed with AI in mind, allowing you to build agents that can
edit documents in realtime and show their presence, in the same way as humans.
For example, in the video below an agent reads the Storage-powered spreadsheet,
highlights the cells it changes, and leaves comments.

<Figure
  caption={
    <>
      An agent making edits in the{" "}
      <UniversalLink href="/examples/ai-spreadsheet/nextjs-ai-spreadsheet">
        AI Spreadsheet
      </UniversalLink>{" "}
      example.
    </>
  }
>
  <MuxVideo
    playbackId="JEQegp4xFuVboJBXdom6croFjrG42aVbbxi01bm5I5Ug"
    alt="Agentic editing"
    static={true}
  />
</Figure>

To edit your Storage document from the server, use [`mutateStorage`][]. Call
[`setPresence`][] first so users can see which cell the agent is editing.

```tsx
const cellId = "B2";

// +++
await liveblocks.mutateStorage("my-room-id", ({ root }) => {
  // +++
  const cells = root.get("cells");

  // Show presence around the cell that's being edited
  // +++
  await liveblocks.setPresence("my-room-id", {
    userId: "agent-123",
    userInfo: { name: "Agent" },
    data: { editing: cellId, status: "Thinking…" },
    ttl: 15,
  });
  // +++

  // Generate an AI response
  const { text: value } = await generateText({
    model: "openai/gpt-5.6-sol",
    prompt: `Fill in cell ${cellId}. Here are all the cells: ${cells.toJSON()}`,
  });

  // Update the cell with the AI response
  // +++
  cells.set(cellId, value);
  // +++
});
```

This is a simple example, but you can take this a step further and, for example,
stream in edits to each cell as an AI generates them too. Learn more about
[Agentic users](/docs/use-cases/agentic-users).

### Version history

Version history is a staple feature for collaborative applications, allowing
users to revert their documents to previous states. Sync supports this, allowing
you to automatically create snapshots at intervals, or manually create them when
you wish. List versions in your app and let users restore them when needed.

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

Version history supports both Storage and Yjs document. To list a room’s
versions use the [`useHistoryVersions`][] hook, and for example to restore a
Storage document, use the [`useRestoreToStorageVersion`][] hook.

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

function VersionsSidebar() {
  const [selectedVersionId, setSelectedVersionId] = useState<string>();
  // +++
  const { versions } = useHistoryVersions();
  const restore = useRestoreToStorageVersion(versionId);
  // +++

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

Versions can be manually created using
[`createVersionHistorySnapshot`](/docs/api-reference/liveblocks-node#create-version-history-snapshot)

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

You can also display previews of old versions. Learn more about
[Version history](/docs/products/sync/version-history).

### Multiplayer undo/redo

Undo and redo are
[notoriously difficult to implement in multiplayer applications](/blog/how-to-build-undo-redo-in-a-multiplayer-environment),
but Sync supports it out of the box, enabling you to build undo/redo buttons
with a few lines of code. When pressing undo, only the current user’s changes
are reverted, and others’ changes are preserved—each user has their own history.

<Figure
  caption={
    <>
      Multiplayer undo/redo in the{" "}
      <UniversalLink href="/examples/collaborative-flowchart-builder/nextjs-flowchart-builder">
        Flowchart Builder
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="hdteCEClBN01u84wsoJdWqlpruyjXwvzQfghj9QNle4c"
    alt="Multiplayer undo/redo in React Flow"
    static={true}
  />
</Figure>

To add undo buttons to your application, simply use [`useUndo`][] to take the
action, and [`useCanUndo`][] to determine whether the action is available.
Similar hooks are available for redo, making this one of the simplest operations
in Sync.

```tsx
import {
  useCanRedo,
  useCanUndo,
  useRedo,
  useUndo,
} from "@liveblocks/react/suspense";

function HistoryButtons() {
  // +++
  const undo = useUndo();
  const redo = useRedo();
  const canUndo = useCanUndo();
  const canRedo = useCanRedo();
  // +++

  return (
    <>
      // +++
      <button onClick={undo} disabled={!canUndo}>
        ↩️ Undo
      </button>
      <button onClick={redo} disabled={!canRedo}>
        ↪️ Redo
      </button>
      // +++
    </>
  );
}
```

You can also choose to pause undo/redo history when a single gesture should
count as a single undo step. For example, when a user drags a shape across a
canvas—it moves 200 pixels, but you wouldn’t want to press undo 200 times.
Pausing history allows you to merge this drag action into one undo action.

```tsx
import { useHistory, useMutation } from "@liveblocks/react/suspense";

function Shape({ index }: { index: number }) {
  // +++
  const { pause, resume } = useHistory();
  // +++

  const moveShape = useMutation(({ storage }, x: number, y: number) => {
    const shape = storage.get("shapes").get(index);
    shape.update({ x, y });
  });

  return (
    <ShapeComponent
      // +++
      onDragStart={() => pause()}
      onDragEnd={() => resume()}
      // +++
      onDragMove={moveShape}
    />
  );
}
```

You can also completely disable undo/redo history, if you’d prefer actions not
to be recorded. Learn more about
[the undo/redo hooks](/docs/api-reference/liveblocks-react#useHistory).

### Permissions

Liveblocks provides a powerful built-in permissions system, allowing you to
control which users, groups, and organizations can read and write to your
document. This is particularly helpful for creating share dialogs, a proven way
to increase MAU, like you’d find in Google Docs or Notion.

<Figure
  caption={
    <>
      Share dialog in the{" "}
      <UniversalLink href="/nextjs-starter-kit">
        Next.js Starter Kit
      </UniversalLink>
    </>
  }
>
  <MuxVideo
    playbackId="5IXSAdPHf2tmGI3QI9fZzUYow1zaMI1LS0239Ler4vaI"
    alt="Share dialog"
    static={true}
    height={520}
    width={768}
  />
</Figure>

When users connect to Liveblocks, they are assigned a userId, groupIds, and
more. You can then use these to control access to your document when creating a
room. In the snippet below, Olivier has full access, but the design team can
only read and not write.

```ts
await liveblocks.createRoom("document-a", {
  // The design team has read-only access
  // +++
  groupsAccesses: {
    "design-team": ["*:read"],
  },
  // +++

  // This user has full access
  // +++
  usersAccesses: {
    olivier: ["*:write"],
  },
  // +++
});
```

You can also create workspaces, and update permissions for a room at any time.
Learn more about [Permissions](/docs/products/sync/permissions).

## Sync integrates into popular tools

You don’t need to build your application from scratch to use Sync, it plugs
directly into the libraries you’re already using. Each integration adds
multiplayer editing and presence around the library’s existing APIs, so you keep
your current setup, and we handle the collaboration.

### Rich-text editors

Add Google Docs-style editing to various rich-text editors, including
[Tiptap][], [BlockNote][], [Lexical][], [ProseMirror][], [Slate][], [Quill][],
and [SuperDoc][]. Each editor has different strengths and abilities, for
example, BlockNote comes with built-in block-based editing, whereas SuperDoc
enables collaborative `.docx` editing.

<Figure
  caption={
    <>
      Editing a <code>.docx</code> file in the{" "}
      <Link href="/examples/collaborative-text-editor/nextjs-yjs-superdoc">
        Collaborative SuperDoc editor
      </Link>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="00u1dOrBat3L01TkOd227C4ffO6LatuSJzqCEdNbyq8LE"
    alt="Collaborative .docx editing with SuperDoc"
    static={true}
  />
</Figure>

### Code editors

Build collaborative coding experiences with [CodeMirror][], which we now support
out of the box, and [Monaco][], the editor that powers VS Code. Ideal for AI
code generation, technical interviews, and pair programming, with each user’s
cursor and selection visible as they type.

<Figure
  caption={
    <>
      Editing with CodeMirror in the{" "}
      <UniversalLink href="/examples/ai-slideshow/nextjs-ai-slideshow">
        AI Slideshow
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="ssmfi62EAy00CqUUU5XX2Cv02mlrFGxiYucqc9bLt02vIs"
    alt="Code editor"
    static={true}
  />
</Figure>

### Canvases

Make whiteboards and drawing canvases multiplayer with [Tldraw][]. Users and
agents can sketch, move shapes, and annotate the same board at once, with every
change merged conflict-free.

<Figure
  caption={
    <>
      Multiplayer editing in the{" "}
      <UniversalLink href="/examples/tldraw-whiteboard/nextjs-tldraw-whiteboard-storage">
        Tldraw Whiteboard
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="sSg2qCJ3rvNNJGa8RxD5aEoC97StjLhy01x00UYJlYPBg"
    alt="Example of a collaborative canvas"
    static={true}
  />
</Figure>

### Flowcharts

Build collaborative diagrams and node-based editors with [React Flow][]. Drag
nodes, draw connections, and watch others’ cursors edit the flow in
realtime—ideal for creating workflow builders.

<Figure
  caption={
    <>
      AI and human collaboration in the{" "}
      <UniversalLink href="/examples/collaborative-flowchart-ai/nextjs-react-flow-ai">
        AI Flowchart
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="00QLfovb3X1B01XKE5wiFWv15iijxDGcZT0177mwP02jVkA"
    alt="Example of a collaborative flowchart"
    static={true}
  />
</Figure>

### Data grids

Turn [Handsontable][] and [AG Grid][] into collaborative spreadsheets. Users can
edit cells simultaneously, see who’s working where, and leave contextual
comments on rows and cells.

<Figure
  caption={
    <>
      Storage and Presence in the{" "}
      <UniversalLink href="/examples/multiplayer-handsontable/nextjs-multiplayer-handsontable">
        Multiplayer Handsontable
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="d01CZy8aLGjHX8TiYjKOfAYWcQ4748Do01Ec69HbYMDGU"
    alt="Example of a collaborative table"
    static={true}
    height={520}
    width={768}
  />
</Figure>

### Chats

Build multiplayer AI chats with chat UI libraries like
[AI Elements](/docs/get-started/nextjs-ai-elements). Every user sees messages
and AI responses streamed live, and can create and switch between shared
conversations.

<Figure
  caption={
    <>
      Multiplayer chat in the{" "}
      <UniversalLink href="/examples/ai-elements-realtime/nextjs-ai-elements-realtime">
        Realtime AI Elements Chats
      </UniversalLink>{" "}
      example
    </>
  }
>
  <MuxVideo
    playbackId="01W5tMAiM2yOColkAFLih8kqYnK3ek0102NYRxv2PAFUt8"
    alt="Multiplayer AI chat built with AI Elements"
    static={true}
  />
</Figure>

## Get started now [#get-started-now]

[Liveblocks Sync][] is available today! Follow our get started guides to add
realtime collaboration to your app in minutes. And if you’d like support
evaluating Liveblocks Sync before starting, schedule a scoping call with our
team.

<div className="flex flex-wrap items-center gap-3">
  <ButtonLink href="/docs/get-started" size="lg" appearance="primary">
    Get started now
  </ButtonLink>
  <ButtonLink href="/contact/sales" size="lg">
    Book a scoping call
  </ButtonLink>
</div>

[Liveblocks Sync]: /docs/products/sync
[Storage]: /docs/products/sync/storage
[Presence]: /docs/products/sync/presence
[Feeds]: /docs/products/sync/feeds
[agentic editing]: /docs/products/sync/agentic-editing
[version history]: /docs/products/sync/version-history
[Server-side editing]: /docs/products/sync/server-side-editing
[Broadcast events]: /docs/products/sync/broadcast-events
[Authentication and permissions]:
  /docs/products/sync/authentication-and-permissions
[`LiveText`]: /docs/api-reference/liveblocks-client#LiveText
[`LiveObject`]: /docs/api-reference/liveblocks-client#LiveObject
[`LiveList`]: /docs/api-reference/liveblocks-client#LiveList
[`LiveMap`]: /docs/api-reference/liveblocks-client#LiveMap
[`LiveFile`]: /docs/api-reference/liveblocks-client#LiveFile
[`useStorage`]: /docs/api-reference/liveblocks-react#useStorage
[`useMutation`]: /docs/api-reference/liveblocks-react#useMutation
[`useOthers`]: /docs/api-reference/liveblocks-react#useOthers
[`useUndo`]: /docs/api-reference/liveblocks-react#useUndo
[`useCanUndo`]: /docs/api-reference/liveblocks-react#useCanUndo
[`useFeedMessages`]: /docs/api-reference/liveblocks-react#useFeedMessages
[`useCreateFeedMessage`]:
  /docs/api-reference/liveblocks-react#useCreateFeedMessage
[`useUpdateMyPresence`]:
  /docs/api-reference/liveblocks-react#useUpdateMyPresence
[`useHistoryVersions`]: /docs/api-reference/liveblocks-react#useHistoryVersions
[`useRestoreToStorageVersion`]:
  /docs/api-reference/liveblocks-react#useRestoreToStorageVersion
[`createFeedMessage`]:
  /docs/api-reference/liveblocks-node#post-rooms-roomId-feeds-feedId-messages
[`updateFeedMessage`]:
  /docs/api-reference/liveblocks-node#put-rooms-roomId-feeds-feedId-messages-messageId
[`mutateStorage`]: /docs/api-reference/liveblocks-node#mutate-storage
[`setPresence`]: /docs/api-reference/liveblocks-node#post-rooms-roomId-presence
[Tiptap]: /docs/get-started/nextjs-tiptap
[BlockNote]: /docs/get-started/nextjs-blocknote
[Lexical]: /docs/get-started/nextjs-lexical
[ProseMirror]: /docs/api-reference/liveblocks-node-prosemirror
[Slate]: /docs/get-started/yjs-slate-react
[Quill]: /docs/get-started/yjs-quill-react
[SuperDoc]: /docs/get-started/yjs-superdoc-nextjs
[CodeMirror]: /docs/get-started/yjs-codemirror-react
[Monaco]: /docs/get-started/yjs-monaco-react
[React Flow]: /docs/get-started/nextjs-react-flow
[Tldraw]: /docs/get-started/nextjs-tldraw
[Handsontable]: /docs/get-started/nextjs-multiplayer-handsontable
[AG Grid]: /docs/get-started/nextjs-comments-ag-grid
[Yjs]: /docs/products/sync/text-editing/yjs