Today, we’re excited to announce Yjs subdocuments support on Liveblocks, two
months after introducing [Liveblocks Yjs](/yjs). This new feature brings a more
dynamic and efficient way to manage collaborative editing and data handling of
Yjs documents.

## Upgrade now [#upgrade-now]

Start using subdocuments now by updating each Liveblocks package in your project
to the `@latest` version.

```bash
 npm install @liveblocks/client@latest @liveblocks/react@latest @liveblocks/yjs@latest liveblocks/node@latest
```

If you use any other Liveblocks packages, make sure to include those too.

## Improve performance by lazy-loading Yjs subdocuments

The integration of Yjs subdocuments into Liveblocks is designed to be as
seamless as possible. Subdocuments introduce a way to lazy-load parts of your
document, making data management more scalable and performant when working with
large documents.

Typically, you’d store different subdocuments within a `Y.Map` in the root
document. This approach helps in keeping document structure both organized and
accessible. Accessing these subdocuments is straightforward via the
`doc.getSubdocs()` function, which returns a `Set<Y.Doc>` of all subdocuments,
even if they’re deeply nested in your data structure. Subdocuments added to your
document will automatically sync with the Liveblocks back end, no additional
coding is required.

### On-demand content loading

One of the key advantages of subdocuments is that they don’t contain any content
upon initial load. This lightweight approach speeds up the loading process and
saves resources. When you need to access the content of a subdocument, simply
call `subdoc.load()` or the utility method `provider.loadSubdoc(guid)`. This
action prompts the Liveblocks server to load and fetch the document. When it’s
complete, a
[`synced`](/docs/api-reference/liveblocks-yjs#LiveblocksProvider.on.sync) event
will fire.

### Responding to document changes

To keep track of subdocuments within your document, users can utilize the
`doc.on("subdocs", {added, removed, loaded}, () => {})` listener. This
functionality is particularly useful for scenarios where you need to load newly
added documents or manage dynamic content changes within your collaborative
session.

### Basic usage example

```javascript
// Create main document and connect to Liveblocks, disabling auto-loading of subdocuments
const doc = new Y.Doc();
const provider = new LiveblocksProvider(room, doc, { autoloadSubdocs: false });

// Create subdocument (note that `subdoc.guid` is its unique identifier)
const subdoc = new Y.Doc();
doc.getMap().set("new doc", subdoc);
subdoc.getText("default").insert(0, "Hello from a Liveblocks Yjs subdocument!");

// From another client, load the subdoc using the GUID from `subdoc.guid` or `doc.getSubdocGuids`
provider.loadSubdoc("my-subdoc-guid");

// Alternatively, get a reference to a subdocument from `doc.getSubdocs()` and then load
subdoc.load();
```

### Manipulating subdocuments via REST API

With Yjs subdocuments, we’re also introducing the capability to manipulate
subdocuments programmatically using the Liveblocks REST API. The API accepts the
`?guid=` parameter, allowing for direct operations on subdocuments. This is
particularly useful for creating AI agents that can act on your documents.

Here’s how you can fetch and update a subdoc using the REST API:

```javascript
async function updateSubdocByGuid(roomId, guid) {
  // Fetch the binary data for a specific subdoc
  const resSubdoc = await fetch(
    `https://api.liveblocks.io/v2/rooms/${roomId}/ydoc-binary?guid=${guid}`,
    {
      method: "GET",
      headers: {
        Authorization: `Bearer ${LIVEBLOCKS_SECRET_KEY}`,
      },
    }
  );

  // Initialize an empty doc
  const subdoc = new Y.Doc();
  const subdocUpdate = await resSubdoc.arrayBuffer();
  Y.applyUpdate(subdoc, new Uint8Array(subdocUpdate));

  // Update the subdoc
  const text = subdoc.getText("test");
  text.insert(0, "Hello from our Rest API!");

  // Send the updated state back to the server
  await fetch(
    `https://api.liveblocks.io/v2/rooms/${roomId}/ydoc?guid=${subdoc.guid}`,
    {
      method: "PUT",
      headers: {
        Authorization: `Bearer ${LIVEBLOCKS_SECRET_KEY}`,
        "Content-Type": "application/octet-stream",
      },
      body: Y.encodeStateAsUpdate(subdoc), // Encode the updated state of the subdoc
    }
  );
}

// Call the function with appropriate roomId, subdoc GUID
updateSubdocByGuid("yourRoomId", "yourSubdocGuid");
```

## Get started with Yjs

If you’d like to get started with Liveblocks Yjs and subdocuments, make sure to
read our guides.

- [Get started with a text editor](/docs/get-started/text-editor)
- [Get started with a code editor](/docs/get-started/code-editor)
- [Assorted Yjs guides](/docs/guides?technologies=yjs)

Remember to [upgrade your packages](#upgrade-now) to start using subdocuments!