# Introduction

Modular building blocks for building collaborative applications like Google Docs and Figma.

{% hint style="info" %}
This documentation website is a work in progress. The best source of information is still the [Yjs README](https://github.com/yjs/yjs) and the [yjs-demos](https://github.com/yjs/yjs-demos) repository.
{% endhint %}

Yjs is a high-performance [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) for building collaborative applications that sync automatically.

It exposes its internal CRDT model as *shared data types* that can be manipulated concurrently. Shared types are similar to common data types like `Map` and `Array`. They can be manipulated, fire events when changes happen, and automatically merge without merge conflicts.

## Quick Start

This is a working example of how shared types automatically sync. We also have a [getting-started guide](/getting-started/a-collaborative-editor), API documentation, and lots of [live demos with source code](https://github.com/yjs/yjs-demos).

```javascript
import * as Y from 'yjs'

// Yjs documents are collections of
// shared objects that sync automatically.
const ydoc = new Y.Doc()
// Define a shared Y.Map instance
const ymap = ydoc.getMap()
ymap.set('keyA', 'valueA')

// Create another Yjs document (simulating a remote user)
// and create some conflicting changes
const ydocRemote = new Y.Doc()
const ymapRemote = ydocRemote.getMap()
ymapRemote.set('keyB', 'valueB')

// Merge changes from remote
const update = Y.encodeStateAsUpdate(ydocRemote)
Y.applyUpdate(ydoc, update)

// Observe that the changes have merged
console.log(ymap.toJSON()) // => { keyA: 'valueA', keyB: 'valueB' }
```

## Editor Support

Yjs supports several popular text and rich-text editors. We are working with different projects to enable collaboration-support through Yjs.

{% content-ref url="/pages/-MArc8impSEg\_q37L\_AV" %}
[ProseMirror](/ecosystem/editor-bindings/prosemirror)
{% endcontent-ref %}

{% content-ref url="/pages/-MW-UWZhHzAQwc19B6Pf" %}
[Tiptap](/ecosystem/editor-bindings/tiptap2)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8ioHyXQ5IGfaDTw" %}
[Monaco](/ecosystem/editor-bindings/monaco)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8ipvsYA7i9vmTav" %}
[Quill](/ecosystem/editor-bindings/quill)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8iq1TIYE-Jvce8v" %}
[CodeMirror](/ecosystem/editor-bindings/codemirror)
{% endcontent-ref %}

{% content-ref url="/pages/-MBL\_t8qqukvwd8pV\_gt" %}
[Remirror](/ecosystem/editor-bindings/remirror)
{% endcontent-ref %}

## Network Agnostic 📡

Yjs doesn't make any assumptions about the network technology you are using. As long as all changes eventually arrive, the documents will sync. The order in which document updates are applied doesn't matter.

You can [integrate Yjs into your existing communication infrastructure](/tutorials/creating-a-custom-provider) or use one of the [several existing network providers](/ecosystem/connection-provider) that allow you to jump-start your application backend.

Scaling shared editing backends is not trivial. Most shared editing solutions depend on a single source of truth - a central server - to perform conflict resolution. Yjs doesn't need a central source of truth. This enables you to design the backend using ideas from distributed system architecture. In fact, Yjs can be scaled indefinitely, as it is shown in the [y-redis section](/tutorials/untitled-3).

If you don't want to maintain your own backend, a number of providers offer Yjs as a service, including [Liveblocks](https://liveblocks.io/yjs), [Y-Sweet](https://jamsocket.com/y-sweet), and [Tiptap](https://tiptap.dev/product/collaboration).

Yjs is truly network agnostic and can be used as a data model for decentralized and [Local-First software](https://www.inkandswitch.com/local-first.html).

Just start somewhere. Since the "network provider" is clearly separated from Yjs and the various integrations, it is pretty easy to switch to different providers.

## Rich Ecosystem 🔥

Yjs is a modular approach that allows the community to make any editor collaborative using any network technology. It has thought-through solutions for almost all shared-editing related problems.

We built a rich ecosystem of extensions around Yjs. There are ready-to-use editor integrations for many popular (rich-)text editors, adapters to different network technologies (like WebRTC, WebSocket, or Hyper), and persistence providers that store document updates in a database.

## Unmatched Performance🚀

Yjs is the fastest CRDT implementation by far.

{% embed url="<https://github.com/dmonad/crdt-benchmarks>" %}


# Yjs in the Wild

Companies using Yjs to achieve amazing collaborative experiences

Yjs is the most-used framework for building collaborative applications. It exceeds over [1M downloads / week](https://www.npmjs.com/package/yjs). The [Yjs README](https://github.com/yjs/yjs) contains a comprehensive list of known users of Yjs. Please create a PR to add your product to the list!

Below is a list of some featured users of Yjs.\\

{% embed url="<https://affine.pro/>" %}

{% embed url="<https://www.gitbook.com/>" %}

{% embed url="<https://evernote.com/>" %}

{% embed url="<https://cargo.site/>" %}

{% embed url="<https://tiptap.dev/>" %}

{% embed url="<https://room.sh/>" %}

{% embed url="<https://github.com/SwiftLaTeX/SwiftLaTeX>" %}

{% embed url="<https://pluxbox.com/theme/yjs>" %}

{% embed url="<https://legendkeeper.com/>" %}

{% embed url="<https://jupyter.org/>" %}

{% embed url="<https://jupyterspot.com/>" %}


# License ❤️

Yjs is permissively licensed ([MIT](https://github.com/yjs/yjs/blob/main/LICENSE)) and actively maintained since 2015 by [Kevin Jahns](https://github.com/dmonad).

{% hint style="info" %}
If you use Yjs in your project, then please do the moral thing and [sponsor further development](https://github.com/sponsors/dmonad) or ask me for a moral license.
{% endhint %}

This project depends on **you** to finance further development and maintenance of the project. Even small donations can go a long way.&#x20;

I offer premium support for people and companies that sponsor me through contracting or [GitHub Sponsors](https://github.com/sponsors/dmonad). If you use Yjs in your company, convince your employer that you need premium support. I will be available for regular video calls,  give feedback, and notify you whenever significant changes come up.

{% embed url="<https://github.com/sponsors/dmonad>" %}


# A Collaborative Editor

A five minute guide to make an editor collaborative

Yjs is a modular framework for syncing things in real-time - like editors!

This guide will walk you through the main concepts of Yjs. First, we are going to create a collaborative editor and sync it with clients. You will get introduced to Yjs documents and to providers, that allow you to sync through different network protocols. Next, we talk about [Awareness & Presence](/getting-started/adding-awareness) which are very important aspects of collaborative software. I created a separate section for [Offline Support](/getting-started/allowing-offline-editing) that shows you how to create offline-ready applications by just adding a few lines of code. The last section is an in-depth guide to [Shared Types](/getting-started/working-with-shared-types).

{% hint style="info" %}
If you are impatient jump to the live demo at the bottom of the page 😉
{% endhint %}

Let's get started by deciding on an editor to use. Yjs doesn't ship with a customized editor. There are already a lot of awesome open-source editors projects out there. Yjs supports many of them using extensions. Editor bindings are a concept in Yjs that allow us to bind the state of a third-party editor to a syncable Yjs document. This is a list of all known editor bindings:

{% content-ref url="/pages/-MArc8il4zP0yza\_1TiZ" %}
[Editor Bindings](/ecosystem/editor-bindings)
{% endcontent-ref %}

For the purpose of this guide, we are going to use the [Quill](https://quilljs.com/) editor - a great rich-text editor that is easy to setup. For a complete reference on how to setup Quill I refer to [their documentation](https://quilljs.com/playground/). If you first require a basic introduction in npm and bundles, please refer to the [webpack getting started guide](https://webpack.js.org/guides/getting-started/) and additionally setting up a [development server](https://webpack.js.org/configuration/dev-server/).

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import Quill from 'quill'
import QuillCursors from 'quill-cursors'

Quill.register('modules/cursors', QuillCursors);

const quill = new Quill(document.querySelector('#editor'), {
  modules: {
    cursors: true,
    toolbar: [
      // adding some basic Quill content features
      [{ header: [1, 2, false] }],
      ['bold', 'italic', 'underline'],
      ['image', 'code-block']
    ],
    history: {
      // Local undo shouldn't undo changes
      // from remote users
      userOnly: true
    }
  },
  placeholder: 'Start collaborating...',
  theme: 'snow' // 'bubble' is also great
})
```

{% endtab %}

{% tab title="HTML" %}

```markup
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">

<div id="editor" />
```

{% endtab %}

{% tab title="Install" %}

```bash
npm i quill quill-cursors
```

{% endtab %}
{% endtabs %}

Next, we are going to install Yjs and the [y-quill](/ecosystem/editor-bindings/quill) editor binding.

```bash
npm i yjs y-quill
```

```javascript
import * as Y from 'yjs'
import { QuillBinding } from 'y-quill'

// A Yjs document holds the shared data
const ydoc = new Y.Doc()
// Define a shared text type on the document
const ytext = ydoc.getText('quill')

// Create an editor-binding which
// "binds" the quill editor to a Y.Text type.
const binding = new QuillBinding(ytext, quill)
```

The `ytext` object is a shared data structure for representing text. It also supports formatting attributes (i.e. **bold** and *italic*). Yjs automatically resolves concurrent changes on shared data so we don't have to worry about conflict resolution anymore. Then we synchronize `ytext` with the `quill` editor and keep them in-sync using the `QuillBinding`. Almost all editor bindings work like this. You can simply exchange the editor binding if you switch to another editor.

But don't stop here, the editor doesn't sync to other clients yet! We need to choose a **provider** or [implement our own communication protocol](/tutorials/creating-a-custom-provider) to exchange document updates with other peers.

{% content-ref url="/pages/-MArc8irNfUawr5IsdyX" %}
[Connection Provider](/ecosystem/connection-provider)
{% endcontent-ref %}

Each provider has pros and cons. The[ y-webrtc](/ecosystem/connection-provider/y-webrtc) provider connects clients directly with each other and is a perfect choice for demo applications because it doesn't require you to set up a server. But for a real-world application, you often want to sync the document to a server. In any case, we got you covered. It is easy to change the provider because they all implement the same interface.

{% tabs %}
{% tab title="y-webrtc" %}

```javascript
import { WebrtcProvider } from 'y-webrtc'

const provider = new WebrtcProvider('quill-demo-room', ydoc)
```

{% endtab %}

{% tab title="y-websocket." %}

```javascript
import { WebsocketProvider } from 'y-websocket'

// connect to the public demo server (not in production!)
const provider = new WebsocketProvider(
  'wss://demos.yjs.dev/ws', 'quill-demo-room', ydoc
)
```

{% endtab %}

{% tab title="y-dat" %}

```javascript
import { DatProvider } from 'y-dat'

// set null in order to create a fresh
const datKey = '7b0d584fcdaf1de2e8c473393a31f52327793931e03b330f7393025146dc02fb'
const provider = new DatProvider(datKey, ydoc)
```

{% endtab %}

{% tab title="Installation" %}

```bash
npm i y-webrtc # or
npm i y-websocket # or
npm i y-dat
```

{% endtab %}
{% endtabs %}

Providers work similarly to editor bindings. They sync Yjs documents through a communication protocol or a database. Most providers have in common that they use the concept of room-names to connect Yjs documents. In the above example, all documents that specify `'quill-demo-room'` as the room-name will sync.

{% hint style="info" %}
**Providers are meshable.** You can connect multiple providers to a Yjs instance at the same time. Document updates will automatically sync through the different communication channels. Meshing providers can improve reliability through redundancy and decrease network delay.
{% endhint %}

By combining Yjs with providers and editor bindings we created our first collaborative editor. In the following sections, we will explore more Yjs concepts like awareness, shared types, and offline editing.

But for now, let's enjoy what we built. I included the same fiddle twice so you can observe the editors sync in real-time. Aware, the editor content is synced with all users visiting this page!

{% embed url="<https://stackblitz.com/edit/y-quill?embed=1&file=index.ts&hideExplorer=1>" %}

{% embed url="<https://stackblitz.com/edit/y-quill?embed=1&file=index.ts&hideExplorer=1>" %}


# Awareness & Presence

Propagating awareness information such as presence &  cursor locations.

Awareness features are an integral part of collaborative applications. In the last chapter, we made an editor collaborative by syncing content among all users. But we can communicate more information to help our users work together. By sharing cursor locations and presence information, we help our users to actively work together. Most applications also assign a unique name and color to each user. This kind of information can be generally classified as "Awareness" information. It gives you hints about what other users are currently doing.

We could share even more awareness information like the mouse position of each user, or a live video recording of each user. But when we share too much information, we distract our users from the task at hand. So it is important to find the right balance, that makes sense for your application.

{% hint style="info" %}
Sharing no awareness information at all is also an option. Then skip this chapter, or come back later.
{% endhint %}

Awareness information isn't stored in the Yjs document, as it doesn't need to be persisted across sessions. Instead, we use a tiny state-based Awareness CRDT that propagates JSON objects to all users. When you go offline, your own awareness state is automatically deleted and all users are notified that you went offline. While this feature is optional, it is recommended that network providers implement the awareness protocol to make it easier to switch providers. All our network providers implement the Awareness CRDT.

In this part of the tutorial we use the Awareness CRDT to render remote cursor locations in the Quill editor. Furthermore, we implement a basic interface to render a list of remote users and assign them a user-name that can be updated in real-time.

## Quick start: Awareness CRDT

The following example shows how you retrieve the Awareness CRDT from the network provider. Then we can set some properties that are propagated to all users. We define the`"user"` property to set the name and preferred color for the current user.

```javascript
// All of our network providers implement the awareness crdt
const awareness = provider.awareness

// You can observe when a user updates their awareness information
awareness.on('change', changes => {
  // Whenever somebody updates their awareness information,
  // we log all awareness information from all users.
  console.log(Array.from(awareness.getStates().values()))
})

// You can think of your own awareness information as a key-value store.
// We update our "user" field to propagate relevant user information.
awareness.setLocalStateField('user', {
  // Define a print name that should be displayed
  name: 'Emmanuelle Charpentier',
  // Define a color that should be associated to the user:
  color: '#ffb61e' // should be a hex color
})
```

The fields of the Awareness CRDT are not standardized. You can set any JSON-encodable value. The editor bindings commonly use the `"cursor"` field to communicate cursor locations. They use the `"user"` field to render the cursor object in a unique color, with the user name above the cursor location.

![Example of y-quill using different colors.](/files/-MLmyFupwLK0HAlEAz7U)

All editor bindings that support rendering cursor information accept an awareness instance to render cursor information. If the `"user"` awareness field is unspecified, then the editor binding will render the cursor in a default color using a random user name.

```javascript
const binding = new QuillBinding(ytext, quill, provider.awareness)
```

For now, we have covered everything we need to know to continue with our tutorial. The complete API documentation for the Awareness CRDT is available in a separate section:

{% content-ref url="/pages/-MArc8jCBNvzOPeJG3hh" %}
[Awareness](/api/about-awareness)
{% endcontent-ref %}

## Adding common awareness features to our collaborative editor

In the below live-code example, I added common awareness features to our editor project. It assigns a random color and a random user-name to each user using the [`do_username`](https://www.npmjs.com/package/do_username) npm package. Furthermore, it renders all present users in a list in their respective colors.

{% embed url="<https://stackblitz.com/edit/y-quill-awareness>" %}

{% embed url="<https://stackblitz.com/edit/y-quill-awareness>" %}


# Offline Support

Adding offline support with y-indexeddb.

We covered that network providers sync document updates over a network protocol to other peers. Database providers sync document updates to a database. The [y-indexeddb](https://github.com/yjs/y-indexeddb) provider syncs document updates to an [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) database - a low-level NoSQL store that is supported by all modern browsers.

Adding offline support in Yjs is as easy as including the [y-indexeddb](https://github.com/yjs/y-indexeddb) provider:

{% tabs %}
{% tab title="Use" %}

```javascript
import { IndexeddbPersistence } from 'y-indexeddb'

const ydoc = new Y.Doc()
const roomName = 'my-room-name'
const persistence = new IndexeddbPersistence(roomName, ydoc)

// The persistence provider works similarly to the network providers:
// const network = new WebrtcProvider(roomName)
```

{% endtab %}

{% tab title="Install" %}

```bash
npm i y-indexeddb --save
```

{% endtab %}
{% endtabs %}

Now every change is persisted to a local database. The next time you visit your site, your document will be loaded from the IndexedDB database. Only the latest changes are synced over the network provider.

You can listen to `synced` events that fire when your client loaded content from the IndexedDB database:

```javascript
persistence.once('synced', () => { console.log('initial content loaded') })
```

Another advantage of using y-indexeddb is that it replicates state to every peer that ever visited the document. In case any peer (e.g. the server) loses some data, the other peers will eventually sync the latest document state back to the server.

{% hint style="info" %}
y-indexeddb works with any other provider. Again, Yjs providers are meshable. You can use several providers at the same time to achieve maximum reliability.
{% endhint %}

Database providers also allow native applications to sync document state to a local database. There is a growing collection of providers that work in different environments available in the [ecosystem section](/ecosystem/database-provider).

### Loading HTML content without network access

In case you manage all application state with Yjs, you'll have an easy time adding offline support to your app. Simply add y-indexeddb and include a service worker to make the website accessible even without network access.

A [service worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) acts like a proxy that persists your HTML/JS/CSS in the web browser. When all data is persisted using service workers, you can even load your website without internet access.

This resource is a great starting point to build your own service worker:

{% embed url="<https://microsoft.github.io/win-student-devs/#/30DaysOfPWA/core-concepts/05>" %}


# Shared Types

By now, we have learned how to make an editor collaborative and sync document updates using different providers. But we haven't covered the most unique feature of Yjs yet: Shared Types.

Shared types are similar to common data types like [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), or [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). The only difference is that they automatically sync & persist their state (using the providers) and that you can observe them.

We already learned about the Y.Text type that we "bound" to an editor instance to sync a rich-text editor automatically. Yjs supports many other shared types like Y.Array, Y.Map, and Y.Xml. A complete list, including documentation for each type, can be found in the [shared types section](/api/shared-types).

Shared type instances must be connected to a Yjs document that syncs them to other peers. First, we define a shared type on a Yjs document. Then, we can manipulate it and observe changes.

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()
// Define an instance of Y.Array named "my array"
// Every peer that defines "my array" like this will sync content with this peer.
const yarray = ydoc.getArray('my array')

// We can register change-observers like this
yarray.observe(event => {
  // Log a delta every time the type changes
  // Learn more about the delta format here: https://quilljs.com/docs/delta/
  console.log('delta:', event.changes.delta)
})

// There are a few caveats that you need to understand when working with shared types
// It is best to explain this in a few lines of code:

// We can insert & delete content
yarray.insert(0, ['some content']) // => delta: [{ insert: ['some content'] }]
// Note that the above method accepts an array of content to insert. 
// So the final document will look like this:
yarray.toArray() // => ['some content']
// We can insert anything that is JSON-encodable. Uint8Arrays also work.
yarray.insert(0, [1, { bool: true }, new Uint8Array([1,2,3])]) // => delta: [{ insert: [1, { bool: true }, Uint8Array([1,2,3])] }]
yarray.toArray() // => [1, { bool: true }, Uint8Array([1,2,3]), 'some content']
// You can even insert Yjs types, enabling you to create nested structures
const subArray = new Y.Array()
yarray.insert(0, [subArray]) // => delta: [{ insert: [subArray] }]
// Note that the above observer doesn't fire when you insert content into subArray
subArray.insert(0, ['nope']) // [observer not called]
// You need to create an observer on subArray instead
subArray.observe(event => { .. })
// Alternatively, you can observe deep changes on yarray (allowing you to observe child events as well)
yarray.observeDeep(events => { console.log('All deep events: ', events) })
subArray.insert(0, ['this works']) // => All deep events: [..]
// You can't insert the array at another place. A shared type can only exist in one place.
yarray.insert(0, [subArray]) // Throws exception!
```

The other data types work similarly to Y.Array. The complete documentation is available in the shared types section, which covers each type and the event format in detail.

{% content-ref url="/pages/-MArc8j3Qm0Nbg4SkR0S" %}
[Shared Types](/api/shared-types)
{% endcontent-ref %}

## Caveats

There are a couple of caveats that you need to look out for.

* A shared type can't be moved to a different position. Once it is "integrated" (inserted as part of the document), you can't integrate it again. Instead, you should create a copy if you need to.
* You can modify a type before integrating it into a Yjs document, but you can't read it. I.e. `new Y.Array([1,2,3]).length === 0` - the type will appear empty until you integrate it into the document.
* You shouldn't modify JSON that you inserted or retrieved from a shared type. Yjs doesn't clone the inserted objects to improve performance. So when you modify a JSON object, you will actually change the internal representation of Yjs without notifying other peers of that change.

```javascript
// 1. An inserted array must not be moved to a different location
yarray.insert(0, ymap.get("my other array") as Y.Array) // will throw an error
// 2. It is discouraged to modify JSON that is inserted or retrieved from a Yjs type
//    This might lead to documents that don't synchronize anymore.
const myObject = { val: 0 }
ymap.set(0, myObject)
ymap.get(0).val = 1 // Doesn't throw an error, but is highly discouraged
myObject.val = 2 // Also doesn't throw an error, but is also discouraged.

```

## Transactions

All changes must happen in a transaction. When you mutate a shared type without creating a transaction (e.g. `yarray.insert(..)`), Yjs will automatically create a transaction before manipulating the shared object. You can create transactions explicitly like this:

```javascript
const ydoc = new Y.Doc()
const ymap = ydoc.getMap('favorites')

// set an initial value - to demonstrate the how changes in ymap are represented
ymap.set('food', 'pizza')

// observers are called after each transaction
ymap.observe(event => {
  console.log('changes', event.changes.keys)
})

ydoc.transact(() => {
  ymap.set('food', 'pencake')
  ymap.set('number', 31)
}) // => changes: Map({ number: { action: 'added' }, food: { action: 'updated', oldValue: 'pizza' } })
```

Event handlers and observers are called after each transaction. If possible, you should bundle as many changes in a single transaction as possible. The advantage is that you reduce expensive observer calls and create fewer updates that are sent to other peers.

Yjs fires events in the following order:

* `ydoc.on('beforeTransaction', event => { .. })` - Called before any transaction, allowing you to store relevant information before changes happen.
* Now the transaction function is executed.
* `ydoc.on('beforeObserverCalls', event => {})`
* `ytype.observe(event => { .. })` - Observers are called.
* `ytype.observeDeep(event => { .. })` - Deep observers are called.
* `ydoc.on('afterTransaction', event => {})` - Called after each transaction.
* `ydoc.on('update', update => { .. })` - This update message is propagated by the providers.

Especially when manipulating many objects, it makes sense to reduce the creation of update messages. So use transactions whenever possible.

## Managing multiple collaborative documents in a shared type

We often want to manage multiple collaborative documents in a single Yjs document. You can manage multiple documents using shared types. In the following demo project, I implemented functionality to add & delete documents. The list of all documents is updated in real time as well.

{% embed url="<https://stackblitz.com/edit/y-quill-doc-list>" %}

{% embed url="<https://stackblitz.com/edit/y-quill-doc-list>" %}

You could extend the above demo project to ..

* .. be able to delete specific documents
* .. have a collaborative document-name. You could introduce a Y.Map that holds the document-name, the document-content, and the creation-date.
* .. extend the document list to a fully-fledged file system based on shared types.

## Conclusion

Shared types are not just great for collaborative editing. They are a unique kind of data structure that can be used to sync any state across servers, browsers, and [native applications](https://github.com/yjs/yrs). Yjs is well suited for creating collaborative applications and gives you all the tools you need to create complex applications that can compete with Google Workspace. We imagine that the concept of shared types could also be exploited in high-performance computing for sharing state across threads or in gaming for syncing data to remote clients directly without a roundtrip to a server. Since Yjs & shared types don't depend on a central server, these data structures are the ideal building blocks for decentralized, privacy-focused applications.

I hope that this section gave you some inspiration for using shared types.


# Editor Bindings

Yjs supports several popular text and rich-text editors. This works by "binding" a shared type to a specific editor instance. Don't worry, you don't have to work with Yjs types if you just want to make something collaborative. But in case you want to build more complex applications, like a folder structure of collaborative documents, read the section about [shared types](/api/shared-types).

{% content-ref url="/pages/-MArc8impSEg\_q37L\_AV" %}
[ProseMirror](/ecosystem/editor-bindings/prosemirror)
{% endcontent-ref %}

{% content-ref url="/pages/-MW-UWZhHzAQwc19B6Pf" %}
[Tiptap](/ecosystem/editor-bindings/tiptap2)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8ioHyXQ5IGfaDTw" %}
[Monaco](/ecosystem/editor-bindings/monaco)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8ipvsYA7i9vmTav" %}
[Quill](/ecosystem/editor-bindings/quill)
{% endcontent-ref %}

{% content-ref url="/pages/-MArc8iq1TIYE-Jvce8v" %}
[CodeMirror](/ecosystem/editor-bindings/codemirror)
{% endcontent-ref %}

{% content-ref url="/pages/-MBL\_t8qqukvwd8pV\_gt" %}
[Remirror](/ecosystem/editor-bindings/remirror)
{% endcontent-ref %}


# ProseMirror

Shared Editing with the ProseMirror Editor

[ProseMirror](https://prosemirror.net) is a fantastic toolkit to build your own richtext editor. [Tiptap](https://github.com/yjs/docs/blob/main/ecosystem/editor-bindings/broken-reference/README.md), [Remirror](/ecosystem/editor-bindings/remirror), and [Atlaskit ](https://atlaskit.atlassian.com/packages/editor/editor-core/example/full-page)are all based on ProseMirror. The [y-prosemirror](https://github.com/yjs/y-prosemirror/) module exports ProseMirror plugins that make any ProseMirror-based editor collaborative. The module even ensures that the document still conforms to the specified schema. The following demo shows how shared editing, cursors, shared undo/redo, and versions can be implemented using the ProseMirror editor toolkit.

{% embed url="<https://github.com/yjs/y-prosemirror/>" %}

### Live Demo

{% embed url="<https://stackblitz.com/edit/y-prosemirror?embed=1&file=index.ts&hideExplorer=1>" %}

{% embed url="<https://stackblitz.com/edit/y-prosemirror?embed=1&file=index.ts&hideExplorer=1>" %}

## yjs-demos

The yjs-demos repository contains multiple demos for the ProseMirror editor. Just clone the directory you are interested in and run `npm install && npm start`.

{% embed url="<https://github.com/yjs/yjs-demos>" %}

## Caveats

Index positions don't work as expected in ProseMirror if you use this module. Instead of indexes, you should use [relative positions](/api/relative-positions) that are based on the Yjs document. Relative positions always point to the place where you originally put them (relatively speaking). In peer-to-peer editing, it is impossible to transform index positions so that everyone ends up with the same positions.

Features such as comments should either be implemented as document state or using relative positions.

A relevant discussion to this topic is found in the ProseMirror discussion board:

{% embed url="<https://discuss.prosemirror.net/t/offline-peer-to-peer-collaborative-editing-using-yjs/2488>" %}

## Versions and showing the differences

This is still a bit experimental, but you can create versions (snapshots of the current state of the document) and show the differences between versions. When we allow offline editing, it is important to show to the user the changes that happened while he was away (a diff of changes). The user can then resolve any potential conflicts. A basic example of how versions can be implemented is shown in the [demos repository](https://github.com/yjs/yjs-demos/tree/master/prosemirror-versions).

{% embed url="<https://demos.yjs.dev/prosemirror-versions/prosemirror-versions.html>" %}
Source code: <https://github.com/yjs/yjs-demos/tree/master/prosemirror-versions>
{% endembed %}

In the basic versions example, we disable garbage collection of deleted content because we want to preserve the deleted strings. This is a problem that is mostly overcome, and you can play with the approach that I implemented on yjs.dev.

In peer-to-peer shared editing, there is no linear history of edits. I suggest that every user preserves its own history of versions. For example, the user could create a version before it receives changes. Another approach is to create versions regularly or let the user handle them. You can share the versions using Yjs types, but this would enforce every user to preserve the complete editing history, although these users might not be interested in all versions.

The yjs.dev website has a ProseMirror example that shows that versions work even with a lot of collaborators. The document has been online since February 2020 and still doesn't slow down. Only content that is relevant to the local version history is preserved.

{% embed url="<https://yjs.dev/>" %}


# Tiptap

Tiptap is a ProseMirror based editor that integrates Yjs as the collaborative editing solution. It has a rich ecosystem of extensions and integrates well into web frameworks (React, Vue, ..).

{% embed url="<https://tiptap.dev>" %}

They have a great getting-started guide for collaborative editing with Yjs & Tiptap:

{% embed url="<https://tiptap.dev/guide/collaborative-editing>" %}


# Monaco

[Monaco](https://microsoft.github.io/monaco-editor/) is the editor that powers VS Code. The [y-monaco](https://github.com/yjs/y-monaco/) extension makes it collaborative.

{% embed url="<https://github.com/yjs/y-monaco/>" %}

{% embed url="<https://microsoft.github.io/monaco-editor/>" %}

## Setup

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
import { MonacoBinding } from 'y-monaco'

const ydoc = new Y.Doc()
const provider = new WebrtcProvider('monaco', ydoc)
const type = ydoc.getText('monaco')

// There are some steps missing to initialize the editor
// The editor requires a webpack build-step
// See the complete example:
//   https://github.com/yjs/yjs-demos/blob/master/monaco/monaco.js
const editor = monaco.editor.create(
  document.getElementById('monaco-editor'),
  {
    value: '',
    language: 'javascript',
    theme: 'vs-dark'
  }
)
const monacoBinding = new MonacoBinding(
  type,
  editor.getModel(),
  new Set([editor]),
  provider.awareness
)

```

{% endtab %}

{% tab title="install" %}

```
npm i monaco-editor yjs y-monaco
```

{% endtab %}
{% endtabs %}

#### Live Demo

Unfortunately, we can't show a live-code example on this website because the editor requires a build-step that online-IDEs don't support. Maybe you can give it a try? Anyway, we still have a live demo available on a different website (simply open it in two different tabs).&#x20;

{% embed url="<https://demos.yjs.dev/monaco/monaco.html>" %}
Live Demo of the y-monaco editor binding
{% endembed %}

#### Demo Code

The [yjs-demos](https://github.com/yjs/yjs-demos) repository contains several demos. Simply download the repository you are interested in (e.g. the `monaco` folder) and run `npm install && npm start` to run the demo.

{% embed url="<https://github.com/yjs/yjs-demos/tree/master/monaco>" %}


# Quill

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-quill/>" %}

### Live Demo

{% embed url="<https://stackblitz.com/edit/y-quill>" %}

{% embed url="<https://stackblitz.com/edit/y-quill>" %}


# CodeMirror

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-codemirror/>" %}

## Live Demo

{% embed url="<https://stackblitz.com/edit/y-codemirror>" %}

{% embed url="<https://stackblitz.com/edit/y-codemirror>" %}


# Remirror

[Remirror](https://remirror.io/) is a ProseMirror based editor that adapts the [`y-prosemirror`](https://github.com/yjs/y-prosemirror/) module to provide collaboration with Yjs.&#x20;

You can try out collaboration live on their website by enabling the "YjsExtension" and duplicating the same window.

{% embed url="<https://remirror.io/playground/>" %}


# Milkdown

Milkdown is a ProseMirror based editor that integrates Yjs as the collaborative editing solution. It's a plugin driven WYSIWYG markdown editor.

{% embed url="<https://milkdown.dev/>" %}
Milkdown Homepage
{% endembed %}

They have a great getting-started guide for collaborative editing with Yjs & Milkdown:

{% embed url="<https://milkdown.dev/docs/guide/collaborative-editing>" %}
Getting Started Guide
{% endembed %}


# Connection Provider

Connection providers handle syncing with the network


# y-websocket

WebSocket Provider for Yjs that ships with a extendable server implementation

The Websocket Provider implements a conventional client-server model. Clients connect to a single endpoint over Websocket. The server distributes document updates and awareness information among clients. You can configure providers on the server as well, which allow you to persist document updates or scale your infrastructure.

The Websocket Provider is a solid choice if you want a central source that handles authentication and authorization. Websockets also send header information and cookies, so you can use existing authentication mechanisms with this server.

* Supports cross-tab communication. When you open the same document in the same browser, changes on the document are exchanged via cross-tab communication ([Broadcast Channel](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) as fallback).
* Supports the exchange of awareness information (e.g. cursors).

{% embed url="<https://github.com/yjs/y-websocket>" %}

## Getting Started

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'

const doc = new Y.Doc()
const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc)

wsProvider.on('status', event => {
  console.log(event.status) // logs "connected" or "disconnected"
})
```

{% endtab %}

{% tab title="Install" %}

```
npm i y-websocket
```

{% endtab %}
{% endtabs %}

### Special case: Using y-websocket in NodeJS

The WebSocket provider requires a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object to create a connection to a server. You can polyfill WebSocket support in Node.js using the [`ws` package](https://www.npmjs.com/package/ws).

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const ws = require('ws')

const wsProvider = new WebsocketProvider(
  'ws://localhost:1234', 'my-roomname',
  doc,
  { WebSocketPolyfill: ws }
)
```

{% endtab %}

{% tab title="Install" %}

```bash
npm i ws
```

{% endtab %}
{% endtabs %}

## API

```javascript
import { WebsocketProvider } from 'y-websocket'
```

**`wsProvider = new WebsocketProvider(serverUrl: string, room: string, ydoc: Y.Doc [, wsOpts: WsOpts])`**\
\*\*\*\* Create a new websocket-provider instance. As long as this provider, or the connected `ydoc`, is not destroyed, the changes will be synced to other clients via the connected server. Optionally, you may specify a configuration object. The following default values of `wsOpts` can be overwritten.

```javascript
wsOpts = {
  // Set this to `false` if you want to connect manually using wsProvider.connect()
  connect: true,
  // Specify a query-string that will be url-encoded and attached to the `serverUrl`
  // I.e. params = { auth: "bearer" } will be transformed to "?auth=bearer"
  params: {}, // Object<string,string>
  // You may polyill the Websocket object (https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).
  // E.g. In nodejs, you could specify WebsocketPolyfill = require('ws')
  WebsocketPolyfill: Websocket,
  // Specify an existing Awareness instance - see https://github.com/yjs/y-protocols
  awareness: new awarenessProtocol.Awareness(ydoc)
}
```

**`wsProvider.wsconnected: boolean`**\
\*\*\*\* True if this instance is currently connected to the server.

**`wsProvider.wsconnecting: boolean`**\
\*\*\*\* True if this instance is currently connecting to the server.

**`wsProvider.shouldConnect: boolean`**\
\*\*\*\* If false, the client will not try to reconnect.

**`wsProvider.bcconnected: boolean`**\
\*\*\*\* True if this instance is currently communicating to other browser-windows via [BroadcastChannel](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel).

**`wsProvider.synced: boolean`**\
\*\*\*\* True if this instance is currently connected and synced with the server.

**`wsProvider.disconnect()`**\
\*\*\*\* Disconnect from the server and don't try to reconnect.

\*\*`wsProvider.connect()` \*\*\
\*\*\*\* Establish a websocket connection to the websocket-server. Call this if you recently disconnected or if you set `wsOpts.connect = false`.

**`wsProvider.destroy()`**\
\*\*\*\* Destroy this `wsProvider` instance. Disconnects from the server and removes all event handlers.

**`wsProvider.on('sync', function(isSynced: boolean))`**\
\*\*\*\* Add an event listener for the `sync` event that is fired when the client received content from the server.

## Websocket Server:

Start a y-websocket server:

```bash
HOST=localhost PORT=1234 npx y-websocket
```

Since npm symlinks the `y-websocket-server` executable from your local `./node_modules/.bin` folder, you can simply run npx. The `PORT` environment variable defaults to 1234.

### Websocket Server with Persistence

Persist document updates in a LevelDB database. See [LevelDB Persistence](/ecosystem/database-provider/y-leveldb) for more information.

```bash
PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js
```

### Websocket Server with HTTP callback

Send a debounced callback to an HTTP server (`POST`) on document update.

Can take the following environment variables:

* `CALLBACK_URL` : Callback server URL
* `CALLBACK_DEBOUNCE_WAIT` : Debounce time between callbacks (in ms). Defaults to 2000 ms
* `CALLBACK_DEBOUNCE_MAXWAIT` : Maximum time to wait before the callback. Defaults to 10 seconds
* `CALLBACK_TIMEOUT` : Timeout for the HTTP call. Defaults to 5 seconds
* `CALLBACK_OBJECTS` : JSON of shared objects to get data (`'{"SHARED_OBJECT_NAME":"SHARED_OBJECT_TYPE}'`)

```bash
CALLBACK_URL=http://localhost:3000/ CALLBACK_OBJECTS='{"prosemirror":"XmlFragment"}' npm start
```

This sends a debounced callback to `localhost:3000` 2 seconds after receiving an update (default `DEBOUNCE_WAIT`) with the data of an XmlFragment named `"prosemirror"` in the body.

### Scaling

These are mere suggestions on how you could scale your server environment. You can use the y-websocket server implementation as a baseline to implement your own scaling approach.

**Option 1:** Websocket servers communicate with each other via a PubSub server. A room is represented by a PubSub channel. The downside of this approach is that the same shared document may be handled by many servers. But the upside is that this approach is fault-tolerant, does not have a single point of failure, and is fit for route balancing.

**Option 2:** Sharding with *consistent hashing*. Each document is handled by a unique server. This pattern requires an entity, like etcd, that performs regular health checks and manages servers. Based on the list of available servers (which is managed by etcd) a proxy calculates which server is responsible for each requested document. The disadvantage of this approach is that load distribution may not be fair. Still, this approach may be the preferred solution if you want to store the shared document in a database - e.g. for indexing.

{% content-ref url="/pages/-MArc8iy2cZ8rAeY6izL" %}
[y-redis](/ecosystem/database-provider/y-redis)
{% endcontent-ref %}


# y-webrtc

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-webrtc>" %}


# y-dat

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-dat>" %}


# Database Provider

Database providers sync documents to a database


# y-indexeddb

IndexedDB database provider for Yjs

Use the IndexedDB database provider to store your shared data persistently in the browser. The next time you join the session, your changes will be loaded from the local browser database.

* Minimizes the amount of data exchanged between server and client
* Makes offline editing possible

{% embed url="<https://github.com/yjs/y-indexeddb>" %}

## Getting Started

The following guide shows you some advanced features of the y-indexeddb database provider. There is a dedicated getting-started guide on creating offline-capable applications with Yjs.

{% content-ref url="/pages/-MArc8ifvSI58\_ypgRvF" %}
[Offline Support](/getting-started/allowing-offline-editing)
{% endcontent-ref %}

{% tabs %}
{% tab title="Use" %}

```javascript
import { IndexeddbPersistence } from 'y-indexeddb'

const provider = new IndexeddbPersistence(docName, ydoc)

provider.on('synced', () => {
  console.log('content from the database is loaded')
})
```

{% endtab %}

{% tab title="Install" %}

```bash
npm i --save y-indexeddb
```

{% endtab %}
{% endtabs %}

## API

**`provider = new IndexeddbPersistence(docName: string, ydoc: Y.Doc)`**\
\*\*\*\* Create a y-indexeddb persistence provider. Specify `docName` as a unique string that identifies this document. In most cases, you want to use the same identifier that is used as the room-name in the connection provider.

**`provider.on('synced', function(idbPersistence: IndexeddbPersistence))`**\
\*\*\*\* The `"synced"` event is fired when the connection to the database has been established and all available content has been loaded. The event is also fired when no content is available yet.

**`provider.set(key: any, value: any): Promise<any>`**\
\*\*\*\* Set a custom property on the provider instance. You can use this to store relevant meta-information for the persisted document. However, the content will not be synced with other peers.

**`provider.get(key: any): Promise<any>`**\
\*\*\*\* Retrieve a stored value.

**`provider.del(key: any): Promise<undefined>`**\
\*\*\*\* Delete a stored value.

**`provider.destroy(): Promise`**\
\*\*\*\* Close the connection to the database and stop syncing the document. This method is automatically called when the Yjs document is destroyed (e.g. `ydoc.destroy()`).

**`provider.clearData(): Promise`**\
\*\*\*\* Destroy this database and remove the stored document and all related meta-information from the database.

## Example

The following example persists document updates to the browsers' database without sharing it with anyone. The content will be persisted across sessions (you can reload the window).

{% embed url="<https://stackblitz.com/edit/y-indexeddb?file=index.ts>" %}


# y-leveldb

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-leveldb>" %}


# y-redis

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-redis>" %}


# Other


# y-protocols

I'm still in the process of moving the documentation to this place. For now, you can find the documentation in the respective README:

{% embed url="<https://github.com/yjs/y-protocols>" %}


# Ports to other languages

Yjs has been ported to other languages.

{% embed url="<https://github.com/yjs/ycs>" %}
C# port of Yjs
{% endembed %}

## ygo

A pure Go port of Yjs. Wire-compatible with the JavaScript reference (V1/V2 update codecs, awareness, snapshot), with the full CRDT engine (YArray, YMap, YText with rich-text formatting and embeds, YXml\*), built-in WebSocket and HTTP sync transports, and an UndoManager. In production at Re:Earth.

{% embed url="<https://github.com/reearth/ygo>" %}
Go port of Yjs
{% endembed %}

## ygo (Deln0r/ygo)

Pure-Go port of Yjs, byte-for-byte wire-compatible with the JavaScript reference in both V1 and V2 update formats, verified bidirectionally by a 114-scenario cross-language fixture suite against <yjs@13.6.x>. Hocuspocus-compatible WebSocket server, SQLite persistence, UndoManager, iOS/Android via gomobile, no CGO.

{% embed url="<https://github.com/Deln0r/ygo>" %}
Go port of Yjs
{% endembed %}

### Work in progress

{% embed url="<https://github.com/yjs/yrs>" %}
Rust port of Yjs (WIP)
{% endembed %}

{% embed url="<https://github.com/yjs/ywasm>" %}
Wasm port of Yjs (WIP)
{% endembed %}


# Y.Doc

```javascript
import * as Y from 'yjs'

const doc = new Y.Doc()
```

## Y.Doc API

**`doc.clientID: number`** (readonly)\
\&#xNAN;*\*\** A unique id that identifies a client for a session. It should not be reused across sessions - see [FAQ](/api/faq#i-get-a-new-clientid-for-every-session-is-there-a-way-to-make-it-static-for-a-peer-accessing-the-document).

**`doc.gc: boolean`**\
Whether garbage collection is enabled on this doc instance. Set `doc.gc = false` to disable garbage collection and be able to restore old content. See [Internals](/api/internals) for more information about how garbage collection works.

**`doc.transact(function(Transaction): void [, origin:any])`**\
Every change on the shared document happens in a transaction. Observer calls and the `update` event are called after each transaction. You should bundle changes into a single transaction to reduce event calls. I.e. `doc.transact(() => { yarray.insert(..); ymap.set(..) })` triggers a single change event.\
You can specify an optional `origin` parameter that is stored on `transaction.origin` and `on('update', (update, origin) => ..)`.

**`doc.get(string, Y.[TypeClass]): [Type]`**\
Get a top-level instance of a shared type.

**`doc.getArray(string = ''): Y.Array`**\
Define a shared Y.Array type. Is equivalent to `y.get(string, Y.Array)`.

**`doc.getMap(string = ''): Y.Map`**\
Define a shared Y.Map type. Is equivalent to `y.get(string, Y.Map)`.

**`doc.getXmlFragment(string = ''): Y.XmlFragment`**\
Define a shared Y.XmlFragment type. Is equivalent to `y.get(string, Y.XmlFragment)`.

**`doc.destroy()`**\
Destroy this Y.Doc instance. All event handlers are cleared and the content is cleared from memory unless it is still being referenced. Bindings and providers that are attached to this document are also destroyed.

**`doc.on(eventName: string, function(event))`**\
Register an [event handler](/api/y.doc#event-handler).

**`doc.once(eventName: string, function(event))`**\
Register an [event handler](/api/y.doc#event-handler). But only call it once.

**`doc.off(eventName: string, function(event))`**\
Unregister an [event handler](/api/y.doc#event-handler).

## Event Handler

**`doc.on('beforeTransaction', function(tr: Transaction, doc: Y.Doc))`**\
The event handler is called right before every transaction.

**`doc.on('beforeObserverCalls', function(tr: Transaction, doc: Y.Doc))`**\
The event handler is called right before observers on shared types are called.

**`doc.on('afterTransaction', function(tr: Transaction, doc: Y.Doc))`**\
The event handler is called right after every transaction.

**`doc.on('update', function(update: Uint8Array, origin: any, doc: Y.Doc, tr: Transaction))`**\
Listen to update messages on the shared document. As long as all update messages are propagated to all users, everyone will eventually consent to the same state. See more about this in the [Document Updates](/api/document-updates) chapter.\
You can generate update messages from the transaction as well, but since creating update messages is relatively expensive we try to generate it once and call this event handler.

**`doc.on('updateV2', function(update: Uint8Array, origin: any, doc: Y.Doc, tr: Transaction))`**\
(EXPERIMENTAL) This is an alternative update message format that is up to 10x more efficient. Should not be used in production.

**`doc.on('subdocs', function(changes: { loaded: Set<Y.Doc>, added: Set<Y.Doc>, removed: Set<Y.Doc> }))`**\
Event is triggered when subdocuments are added/removed or loaded. See [Subdocuments](/api/subdocuments) on how this event can be used.

**`doc.on('destroy', function(doc: Y.Doc))`**\
The event handler is called just before the Y.Doc is destroyed. Bindings and providers should listen to this event and destroy themselves when the event is called.

## Order of events

All changes to a Yjs document / shared types happen in a transaction. Events are called in the following order:

1. `ydoc.on('beforeTransaction', event => { .. })`
2. The transaction is executed.
3. `ydoc.on('beforeObserverCalls', event => {})`
4. `ytype.observe(event => { .. })`&#x20;
5. `ytype.observeDeep(event => { .. })`&#x20;
6. `ydoc.on('afterTransaction', event => {})`&#x20;
7. `ydoc.on('update', update => { .. })`&#x20;


# Shared Types


# Y.Map

A shared type with a similar API to global.Map

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.Map as a top-level type or a nested type

// Method 1: Define a top-level type
const ymap = ydoc.getMap('my map type') 
// Method 2: Define Y.Map that can be included into the Yjs document
const ymapNested = new Y.Map()

// Nested types can be included as content into any other shared type
ymap.set('my nested map', ymapNested)

// Common methods
ymap.set('prop-name', 'value') // value can be anything json-encodable
ymap.get('prop-name') // => 'value'
ymap.delete('prop-name')
```

## API

**`ymap.doc: Y.Doc | null`** (readonly)\
The Yjs document that this type is bound to. Is `null` when it is not bound yet.

**`ymap.parent: Y.AbstractType | null`**\
The parent that holds this type. Is `null` if this `ymap` is a top-level type.

**`ymap.set(key: string, value: object|boolean|string|number|Uint8Array|Y.AbstractType)`**\
Add or update an entry with a specified key. This method works similarly to the [Map.set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) method. The value can be a shared type, an Uint8Array, or anything JSON-encodable.

**`ymap.get(key: string): object|boolean|Array|string|number|Uint8Array|Y.AbstractType`**\
Returns an entry with the specified key. This method works similarly to the [Map.get](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) method.

**`ymap.delete(key: string)`**\
Deletes an entry with the specified key. This method works similarly to the [Map.delete](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) method.

**`ymap.has(key: string): boolean`**\
Returns true if an entry with the specified key exists. This method works similarly to the[ Map.has](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) method.

**`ymap.clear()`**\
Removes all elements from this `ymap`.

**`ymap.toJSON(): Object<string,object|boolean|Array|string|number|Uint8Array>`**\
Copies the `[key,value]` pairs of this Y.Map to a new Object. It transforms all shared types to JSON using their `toJSON` method.

**`ymap.size: number`**\
Returns the number of key/value pairs.

**`ymap.forEach(value: any, key: string, map: Y.Map)`**\
Execute the provided function once on every key-value pair.

**`ymap[Symbol.Iterator]: Iterator`**\
Returns an [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of `[key, value]` pairs. This allows you to iterate over the `ymap` using a for..of loop: `for (const [key, value] of ymap) { .. }`

**`ymap.entries(): Iterator`**\
Returns an [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of `[key, value]` pairs.

**`ymap.values(): Iterator`**\
Returns an [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of values only. This allows you to iterate through the values only `for (const value of ymap.values()) { ... }` or insert all values into an array `Array.from(ymap.values())`.

**`ymap.keys(): Iterator`**\
Returns an [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of keys only. This allows you to iterate through the keys only `for (const key of ymap.keys()) { ... }` or insert all keys into an array `Array.from(ymap.keys())`.

**`ymap.clone(): Y.Map`**\
Clone all values into a fresh Y.Map instance. The returned type can be included into the Yjs document.

**`ymap.observe(function(YMapEvent, Transaction))`**\
Registers a change observer that will be called synchronously every time this shared type is modified. In the case this type is modified in the observer call, the event listener will be called again after the current event listener returns.

**`ymap.unobserve(function)`**\
Unregisters a change observer that has been registered with `ymap.observe`.

**`ymap.observeDeep(function(Array<Y.Event>, Transaction))`**\
Registers a change observer that will be called synchronously every time this type or any of its children is modified. In the case this type is modified in the event listener, the event listener will be called again after the current event listener returns. The event listener receives all Events created by itself or any of its children.

**`ymap.unobserveDeep(function)`**\
Unregisters a change observer that has been registered with `ymap.observeDeep`.

## Observing changes: Y.MapEvent

```javascript
ymap.observe(ymapEvent => {
  ymapEvent.target === ymap // => true

  // Find out what changed: 
  // Option 1: A set of keys that changed
  ymapEvent.keysChanged // => Set<strings>
  // Option 2: Compute the differences
  ymapEvent.changes.keys // => Map<string, { action: 'add'|'update'|'delete', oldValue: any}>

  // sample code.
  ymapEvent.changes.keys.forEach((change, key) => {
    if (change.action === 'add') {
      console.log(`Property "${key}" was added. Initial value: "${ymap.get(key)}".`)
    } else if (change.action === 'update') {
      console.log(`Property "${key}" was updated. New value: "${ymap.get(key)}". Previous value: "${change.oldValue}".`)
    } else if (change.action === 'delete') {
      console.log(`Property "${key}" was deleted. New value: undefined. Previous value: "${change.oldValue}".`)
    }
  })
})

ymap.set('key', 'value') // => Property "key" was added. Initial value: "value".
ymap.set('key', 'new') // => Property "key" was updated. New value: "new". Previous value: "value".
ymap.delete('key') // => Property "key" was deleted. New value: undefined. Previous Value: "new".
```

### Y.MapEvent API

`ymapEvent.keysChanged: Set<string>`\
A [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) containing all keys that were modified during a transaction.

See [Y.Event](/api/y.event) API. The rest of the API is inherited from Y.Event.


# Y.Array

A shared type to store data in a sequence-like data structure

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.Array as a top-level type or a nested type

// Method 1: Define a top-level type
const yarray = ydoc.getArray('my array type') 
// Method 2: Define Y.Array that can be included into the Yjs document
const yarrayNested = new Y.Array()

// Nested types can be included as content into any other shared type,
// notice that a shared type can only exist once in a document.
yarray.insert(0, [yarrayNested])

// Common methods
yarray.insert(0, [1, 2, 3]) // insert three elements
yarray.delete(1, 1) // delete second element 
yarray.toArray() // => [1, 3]
```

## API

**`Y.Array.from(Array<JSON | Uint8Array | Y.AbstractType>): Y.Array`**\
An alternative factory function to create a Y.Array based on existing content.

**`yarray.doc: Y.Doc | null`** (readonly)\
The Yjs document that this type is bound to. Is `null` when it is not bound yet.

**`yarray.parent: Y.AbstractType | null`**\
The parent that holds this type. Is `null` if this `yarray` is a top-level type.

**`yarray.length: number`**\
The number of elements that this Y.Array holds.

**`yarray.insert(index: number, content: Array<JSON | Uint8Array | Y.AbstractType>)`**\
Insert content at a specified `index`. Note that - for performance reasons - content is always an array of elements. I.e. `yarray.insert(0, [1])` inserts 1 at position 0.

**`yarray.delete(index: number, length: number)`**\
Delete `length` Y.Array elements starting from `index`.

**`yarray.push(content: Array<JSON | Uint8Array | Y.AbstractType>)`**\
Append content at the end of the Y.Array. Same as `yarray.insert(yarray.length, content)`.

**`yarray.unshift(content: Array<JSON | Uint8Array | Y.AbstractType>)`**\
Prepend content to the beginning of the Y.Array. Same as `yarray.insert(0, content)`.

**`yarray.get(index: number): JSON | Uint8Array | Y.AbstractType`**\
Retrieve the n-th element.

**`yarray.slice([start: number [, end: number]]): Array<JSON | Uint8Array | Y.AbstractType>`**\
Retrieve a range of content starting from index `start` (inclusive) to index `end` (exclusive). Negative indexes can be used to indicate offsets from the end of the Y.Array. I.e. `yarray.slice(-1)` returns the last element. `yarray.slice(0, -1)` returns all but the last element. Works similarly to the [Array.slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) method.

**`yarray.toArray(): Array<JSON | Uint8Array | Y.AbstractType>`**\
Copies the content of the Y.Array to a new Array.

**`yarray.toJSON(): Array<JSON | Uint8Array>`**\
Retrieve the JSON representation of this type. The result is a fresh Array that contains all Y.Array elements. Elements that are shared types are transformed to JSON as well, using their `toJSON` method. The result may contain Uint8Arrays which are not JSON-encodable.

**`yarray.forEach(function(value: any, index: number, yarray: Y.Array))`**\
Execute the provided function once on every element.

**`yarray.map(function(value: any, index: number, yarray: Y.Array): T): Array<T>`**\
Creates a new Array filled with the results of calling the provided function on each element in the Y.Array.

**`yarray[Symbol.Iterator]: Iterator`**\
Returns an [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of values for the Y.Array. This allows you to iterate over the `yarray` using a for..of loop: `for (const value of yarray) { .. }`

**`yarray.clone(): Y.Array`**\
Clone all values into a fresh Y.Array instance. The returned type can be included into the Yjs document.

**`yarray.observe(function(YArrayEvent, Transaction))`**\
Registers a change observer that will be called synchronously every time this shared type is modified. In the case this type is modified in the observer call, the event listener will be called again after the current event listener returns.

**`yarray.unobserve(function)`**\
Unregisters a change observer that has been registered with `yarray.observe`.

**`yarray.observeDeep(function(Array<Y.Event>, Transaction))`**\
Registers a change observer that will be called synchronously every time this type or any of its children is modified. In the case this type is modified in the event listener, the event listener will be called again after the current event listener returns. The event listener receives all Events created by itself or any of its children.

**`yarray.unobserveDeep(function)`**\
Unregisters a change observer that has been registered with `yarray.observeDeep`.

## Observing changes: Y.ArrayEvent

The `yarray.observe` callback fires `Y.ArrayEvent` events that you can use to calculate the changes that happened during a transaction. We use an adaption of the [Quill delta format](https://quilljs.com/docs/delta/) to calculate the differences. Instead of strings, our ArrayDelta works on Arrays. You can find more examples and information about the delta format in our [Y.Event API](/api/y.event#delta-format).

```javascript
yarray.observe(yarrayEvent => {
  yarrayEvent.target === yarray // => true

  // Find out what changed: 
  // Log the Array-Delta Format to calculate the difference to the last observe-event
  console.log(yarrayEvent.changes.delta)
})

yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }]
yarray.delete(2, 1) // [{ retain: 2 }, { delete: 1 }]

console.log(yarray.toArray()) // => [1, 2]

// The delta-format is very useful when multiple changes
// are performed in a single transaction
ydoc.transact(() => {
  yarray.insert(1, ['a', 'b'])
  yarray.delete(2, 2) // deletes 'b' and 2
}) // => [{ retain: 1 }, { insert: ['a'] }, { delete: 1 }]

console.log(yarray.toArray()) // => [1, 'a']
```

### Y.ArrayEvent API

See [Y.Event](/api/y.event) API. The API is inherited from Y.Event.I'm still in the process of moving the documentation to this place. For now, you can find the API docs in the README:

{% embed url="<https://github.com/yjs/yjs#API>" %}
API DOCS
{% endembed %}


# Y.Text

A shared type that represents Text & RichText

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.Text as a top-level type or a nested type

// Method 1: Define a top-level type
const ytext = ydoc.getText('my text type') 
// Method 2: Define Y.Text that can be included into the Yjs document
const ytextNested = new Y.Text()

// Nested types can be included as content into any other shared type
ydoc.getMap('another shared structure').set('my nested text', ytextNested)

// Common methods
ytext.insert(0, 'abc') // insert three elements
ytext.format(1, 2, { bold: true }) // delete second element 
ytext.toString() // => 'abc'
ytext.toDelta() // => [{ insert: 'a' }, { insert: 'bc', attributes: { bold: true }}]
```

## API

**`ytext = new Y.Text(initialContent): Y.Text`**\
Create an instance of Y.Text with existing content.

**`ytext.doc: Y.Doc | null`** (readonly)\
The Yjs document that this type is bound to. Is `null` when it is not bound yet.

**`ytext.parent: Y.AbstractType | null`** (readonly)\
The parent that holds this type. Is `null` if this `ytext` is a top-level type.

**`ytext.length: number`** (readonly)\
The length of the string in UTF-16 code units. Since JavaScripts' String implementation uses the same character encoding `ytext.toString().length === ytext.length`.

**`ytext.insert(index: number, content: string[, format: Object<string,any>])`**\
Insert content at a specified `index`. Optionally, you may specify formatting attributes that are applied to the inserted string. By default, the formatting attributes before the insert position will be used.

**`ytext.format(index: number, length: number, format: Object<string,any>)`**\
Assign formatting attributes to a range of text.

**`ytext.applyDelta(delta: Delta)`**\
Apply a Text-Delta to the Y.Text instance.

**`ytext.delete(index: number, length: number)`**\
Delete `length` characters starting from `index`.

**`ytext.toString(): string`**\
Retrieve the string-representation (without formatting attributes) from the Y.Text instance.

**`ytext.toDelta(): Delta`**\
Retrieve the Text-Delta-representation of the Y.Text instance. The Text-Delta is equivalent to [Quills' Delta format](https://quilljs.com/docs/delta/).

**`ytext.toJSON(): string`**\
Retrieves the string representation of the Y.Text instance.

**`ytext.clone(): Y.Text`**\
Clone this type into a fresh Y.Text instance. The returned type can be included into the Yjs document.

**`ytext.observe(function(YTextEvent, Transaction))`**\
Registers a change observer that will be called synchronously every time this shared type is modified. In the case this type is modified in the observer call, the event listener will be called again after the current event listener returns.

**`ytext.unobserve(function)`**\
Unregisters a change observer that has been registered with `ytext.observe`.

**`ytext.observeDeep(function(Array<Y.Event>, Transaction))`**\
Registers a change observer that will be called synchronously every time this type or any of its children is modified. In the case this type is modified in the event listener, the event listener will be called again after the current event listener returns. The event listener receives all Events created by itself or any of its children.

**`ytext.unobserveDeep(function)`**\
Unregisters a change observer that has been registered with `ytext.observeDeep`.

## Delta Format

\[todo]

\[formatting attributes]

## Observing changes: Y.TextEvent

\[todo]

```javascript
yarray.observe(yarrayEvent => {
  yarrayEvent.target === yarray // => true

  // Find out what changed: 
  // Option 1: A set of keys that changed
  ymapEvent.keysChanged // => Set<strings>
  // Option 2: Compute the differences
  ymapEvent.changes.keys // => Map<string, { action: 'add'|'update'|'delete', oldValue: any}>
  
  // sample code.
  yarrayEvent.changes.keys.forEach((change, key) => {
    if (change.action === 'add') {
      console.log(`Property "${key}" was added. Initial value: "${ymap.get(key)}".`)
    } else if (change.action === 'update') {
      console.log(`Property "${key}" was updated. New value: "${ymap.get(key)}". Previous value: "${change.oldValue}".`)
    } else if (change.action === 'delete') {
      console.log(`Property "${key}" was deleted. New value: undefined. Previous value: "${change.oldValue}".`)
    }
  })
})

ymap.set('key', 'value') // => Property "key" was added. Initial value: "value".
ymap.set('key', 'new') // => Property "key" was updated. New value: "new". Previous value: "value".
ymap.delete('key') // => Property "key" was deleted. New value: undefined. Previous Value: "new".

```

### Y.TextEvent API

See [Y.Event](/api/y.event) API. The API is inherited from Y.Event.I'm still in the process of moving the documentation to this place. For now, you can find the API docs in the README:


# Y.XmlFragment

A shared type to manage a collection of Y.Xml\* Nodes

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.XmlFragment as a top-level type or a nested type

// Method 1: Define a top-level type
const yxmlFragment = ydoc.getXmlFragment('my xml fragment')
// Method 2: Define Y.XmlFragment that can be included into the Yjs document
const yxmlNested = new Y.XmlFragment()

// Common methods
const yxmlText = new Y.XmlText()
yxmlFragment.insert(0, [yxmlText])
yxmlFragment.firstChild === yxmlText
yxmlFragment.insertAfter(yxmlText, [new Y.XmlElement('node-name')])
yxmlFragment.get(0) === yxmlText // => true

//show result in dev console
console.log(yxmlFragment.toDOM())
```

## API

**`yxmlFragment.doc: Y.Doc | null`** (readonly)\
The Yjs document that this type is bound to. Is `null` when it is not bound yet.

**`yxmlFragment.parent: Y.AbstractType | null`**\
The parent that holds this type. Is `null` if this `yxmlFragment` is a top-level type.

**`yxmlFragment.firstChild: Y.XmlElement | Y.XmlText | null`**\
The first child that holds this type holds. Is `null` if this type doesn't hold any children.

**`yxmlFragment.length: number`**\
The number of child-elements that this Y.XmlFragment holds.

**`yxmlFragment.insert(index: number, content: Array<Y.XmlElement | Y.XmlText>)`**\
Insert content at a specified `index`. Note that - for performance reasons - content is always an array of elements. I.e. `yxmlFragment.insert(0, [new Y.XmlElement()])` inserts a single element at position 0.

**`yxmlFragment.insertAfter(ref: Y.XmlElement | Y.XmlText | null, content: Array<Y.XmlElement | Y.XmlText>)`**\
Insert content after a reference element. If the reference element `ref` is null, then the content is inserted at the beginning.

**`yxmlFragment.delete(index: number, length: number)`**\
Delete `length` elements starting from `index`.

**`yxmlFragment.push(content: Array<Y.XmlElement | Y.XmlText>)`**\
Append content at the end of the Y.XmlElement. Equivalent to `yxmlFragment.insert(yxmlFragment.length, content)`.

**`yxmlFragment.unshift(content: Array<Y.XmlElement | Y.XmlText>)`**\
Prepend content to the beginning of the Y.Array. Same as `yxmlFragment.insert(0, content)`.

**`yxmlFragment.get(index: number): Y.XmlElement | Y.XmlText`**\
Retrieve the n-th element.

**`yxmlFragment.slice([start: number [, end: number]]): Array<Y.XmlElement | Y.XmlText>`**\
Retrieve a range of content starting from index `start` (inclusive) to index `end` (exclusive). Negative indexes can be used to indicate offsets from the end of the Y.XmlFragment. I.e. `yxmlFragment.slice(-1)` returns the last element. `yxmlFragment.slice(0, -1)` returns all but the last element. Works similarly to the [Array.slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) method.

**`yxmlFragment.toJSON(): String`**\
Retrieve the JSON representation of this type. The result is a concatenated string of XML elements.

Example:

```xml
<element1>foo</element1><element2>bar</element2>
```

If the fragment contains more than one XML element, the output will not be a valid XML; It will need to be placed inside a container element to be valid and parsable. Example:

```js
const validDocument = `<wrapper>${yXmlFragment.toJSON()}</wrapper>`
```

**`yxmlFragment.createTreeWalker(filter: function(yxml: Y.XmlElement | Y.XmlText): boolean): Iterable`**\
Create an [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) that walks through all children of this type (not only direct children). The returned iterable returns every element that the filter accepts. I.e. the following code iterates through all `Y.XmlElements` that have the node-name `'p'`.

```javascript
// Log all <p> nodes that are children of this Y.XmlFragment
for (const paragraph of yxmlFragment.createTreeWalker(yxml => yxml.nodeName === 'p')) {
  ..
}
```

**`yxmlFragment.clone(): Y.XmlFragment`**\
Clone all values into a fresh Y.XmlFragment instance. The returned type can be included into the Yjs document.

**toDOM():DocumentFragment** Transforms this type and all children to new DOM elements.

**`yxmlFragment.observe(function(Y.XmlEvent, Transaction))`**\
Registers a change observer that will be called synchronously every time this shared type is modified. In the case this type is modified in the observer call, the event listener will be called again after the current event listener returns.

**`yxmlFragment.unobserve(function)`**\
Unregisters a change observer that has been registered with `yxmlFragment.observe`.

**`yxmlFragment.observeDeep(function(Array<Y.Event>, Transaction))`**\
Registers a change observer that will be called synchronously every time this type or any of its children is modified. In the case this type is modified in the event listener, the event listener will be called again after the current event listener returns. The event listener receives all Events created by itself and any of its children.

**`yxmlFragment.unobserveDeep(function)`**\
Unregisters a change observer that has been registered with `yxmlFragment.observeDeep`.

## Observing changes: Y.XmlEvent

The `yxmlFragment.observe` callback fires `Y.XmlEvent` events that you can use to calculate the changes that happened during a transaction. We use an adaption of the [Quill delta format](https://quilljs.com/docs/delta/) to calculate insertions & deletions of child-elements. You can find more examples and information about the delta format in our [Y.Event API](/api/y.event#delta-format).

```javascript
yxmlFragment.observe(yxmlElent => {
  yxmlEvent.target === yarray // => true

  // Observe when child-elements are added or deleted. 
  // Log the Xml-Delta Format to calculate the difference to the last observe-event
  console.log(yxmlEvent.changes.delta)
})

yxmlFragment.insert(0, [new Y.XmlText()]) // => [{ insert: [yxmlText] }]
yxmlFragment.delete(0, 1) // [{ delete: 1 }]
```

### Y.XmlEvent API

\[todo]

See [Y.Event](/api/y.event) API. The API is inherited from Y.Event.I'm still in the process of moving the documentation to this place. For now, you can find the API docs in the README:

{% embed url="<https://github.com/yjs/yjs#API>" %}
API DOCS
{% endembed %}


# Y.XmlElement

A shared type that represents an XML node

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.XmlElement as a top-level type or a nested type

// Method 1: Define a top-level type
// Note that the nodeName is always "undefined"
// when defining an XmlElement as a top-level type.
const yxmlElement = ydoc.get('prop-name', Y.XmlElement)
// Method 2: Define Y.XmlFragment that can be included into the Yjs document
const yxmlNested = new Y.XmlElement('node-name')

// Common methods
const yxmlText = new Y.XmlText()
yxmlFragment.insert(0, [yxmlText])
yxmlFragment.firstChild === yxmlText
yxmlFragment.insertAfter(yxmlText, [new Y.XmlElement('node-name')])
yxmlFragment.get(0) === yxmlText // => true

//show result in dev console
console.log(yxmlFragment.toDOM())
```

## API

> Inherits from [Y.XmlFragment](/api/shared-types/y.xmlfragment).

**`const yxmlElement = Y.XmlElement(nodeName: string)`**

**`yxmlElement.nodeName: string`**\
The name of this Y.XmlElement as a String.

**`yxmlElement.prevSibling: Y.XmlElement | Y.XmlText | null`**\
The previous sibling of this type. Is null if this is the first child of its parent.

**`yxmlElement.nextSibling: Y.XmlElement | Y.XmlText | null`**\
The next sibling of this type. Is null if this is the last child of its parent.

**`yxmlElement.toString(): string`**\
Returns the XML-String representation of this element. E.g. `"<div height="30px"></div>"`

**`yxmlElement.setAttribute(name: string, value: string | Y.AbstractType)`**\
Set an XML attribute. Technically, the value can only be a string. But we also allow shared types. In this case, the XML type can't be properly converted to a string.

**`yxmlElement.removeAttribute(name: string)`**\
Remove an XML attribute.

**`yxmlElement.getAttribute(name: string): string | Y.AbstractType`**\
Retrieve an XML attribute.

**`yxmlElement.getAttributes(): Object<string, string | Y.AbstractType>`**\
Retrieve all XML attributes.

## Observing changes: Y.XmlEvent

The `yxmlElement.observe` callback fires `Y.XmlEvent` events that you can use to calculate the changes that happened during a transaction. We use an adaption of the [Quill delta format](https://quilljs.com/docs/delta/) to calculate insertions & deletions of child-elements. Changes on the xml-attributes are expressed using the same API from [Y.Map](/api/shared-types/y.map#observing-changes-y-mapevent).

```javascript
yxmlFragment.observe(yxmlEvent => {
  yxmlEvent.target === yarray // => true

  // Observe when child-elements are added or deleted. 
  // Log the Xml-Delta Format to calculate the difference to the last observe-event
  console.log(yxmlEvent.changes.delta)

  // Observe attribute changes.  
  // Option 1: A set of keys that changed
  yxmlEvent.keysChanged // => Set<strings>
  // Option 2: Compute the differences
  yxmlEvent.changes.keys // => Map<string, { action: 'add'|'update'|'delete', oldValue: any}>

  // The change format is equivalent to the Y.MapEvent change format.
  yxmlEvent.changes.keys.forEach((change, key) => {
    if (change.action === 'add') {
      console.log(`Attribute "${key}" was added. Initial value: "${ymap.get(key)}".`)
    } else if (change.action === 'update') {
      console.log(`Attribute "${key}" was updated. New value: "${ymap.get(key)}". Previous value: "${change.oldValue}".`)
    } else if (change.action === 'delete') {
      console.log(`Attribute "${key}" was deleted. New value: undefined. Previous value: "${change.oldValue}".`)
    }
  })
})

yxmlElement.insert(0, [new Y.XmlText()]) // => [{ insert: [yxmlText] }]
yxmlElement.delete(0, 1) // [{ delete: 1 }]

yxmlElement.setAttribute('key', 'value') // Attribute "key" was added. Initial value: "undefined".
yxmlElement.setAttribute('key', 'new value') // Attribute "key" was updated. New value: "new value". Previous value: "value"
yxmlElement.deleteAttribute('key') // Attribute "key" was deleted. New value: undefined. Previous value: "new value"
```

### Y.XmlEvent API

\[todo]

describe childListChanged and attributesChanged

See [Y.Event](/api/y.event) API. The API is inherited from Y.Event.I'm still in the process of moving the documentation to this place. For now, you can find the API docs in the README:

{% embed url="<https://github.com/yjs/yjs#API>" %}
API DOCS
{% endembed %}


# Y.XmlText

Extends Y.Text to represent a Y.Xml node.

```javascript
import * as Y from 'yjs'

const ydoc = new Y.Doc()

// You can define a Y.XmlText as a top-level type or a nested type

// Method 1: Define a top-level type
const yxmlText = ydoc.get('my xmltext type', Y.XmlText) 
// Method 2: Define Y.XmlText that can be included into the Yjs document
const yxmltextNested = new Y.XmlText()

// Nested types can be included as content into any other shared type
yxmlText.set('my nested text', ytextNested)

// Common methods (also available in Y.Text)
yxmlText.insert(0, 'abc') // insert three elements
yxmlText.format(1, 2, { bold: true }) // delete second element 
yxmlText.toDelta() // => [{ insert: 'a' }, { insert: 'bc', attributes: { bold: true }}]

// Methods specific to Y.XmlText
yxmlText.prevSibling
yxmlText.nextSibling
yxmlText.toString() // => "a<bold>bc</bold>"
```

## API

> Inherits from [Y.Text](/api/shared-types/y.text).

**`const yxmlText = Y.XmlText()`**

**`yxmlText.prevSibling: Y.XmlElement | Y.XmlText | null`**\
The previous sibling of this type. Is null if this is the first child of its parent.

**`yxmlText.nextSibling: Y.XmlElement | Y.XmlText | null`**\
The next sibling of this type. Is null if this is the last child of its parent.

**`yxmlText.toString(): string`**\
Returns the XML-String representation of this element. Formatting attributes are transformed to XML-tags. If the formatting attribute contains an object, the key-value pairs will be used as attributes. E.g.

```javascript
ymxlText.insert(0, "my link", { a: { href: 'https://..' } })
ymxlText.toString() // => <a href="https://..">my link</a>
```


# Y.UndoManager

A selective Undo/Redo manager for Yjs.

Yjs ships with a selective Undo/Redo manager. The changes can be optionally scoped to transaction origins.

```javascript
import * as Y from 'yjs'

const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext)

ytext.insert(0, 'abc')
undoManager.undo()
ytext.toString() // => ''
undoManager.redo()
ytext.toString() // => 'abc'
```

**`const undoManager = new Y.UndoManager(scope: Y.AbstractType | Array<Y.AbstractType> [, {captureTimeout: number, trackedOrigins: Set<any>, deleteFilter: function(item):boolean}])`**\
Creates a new Y.UndoManager on a scope of shared types. If any of the specified types, or any of its children is modified, the UndoManager adds a reverse-operation on its stack. Optionally, you may specify `trackedOrigins` to track changes from different sources. By default, all local changes that don't specify a transaction `origin` will be tracked. The UndoManager merges edits that are created within a certain `captureTimeout` (defaults to 500ms). Set it to 0 to capture each change individually.

**`undoManager.undo()`**\
Undo the last operation on the UndoManager stack. The reverse operation will be put on the redo-stack.

**`undoManager.redo()`**\
Redo the last operation on the redo-stack. I.e. the previous undo is reversed.

**`undoManager.stopCapturing()`**\
Call `stopCapturing()` to ensure that the next operation that is put on the UndoManager is not merged with the previous operation.

**`undoManager.clear()`**\
Delete all captured operations from the undo & redo stack.

**`undoManager.on('stack-item-added', function({stackItem, origin, type:'undo'|'redo', changedParentTypes}, undoManager))`**\
Register an event handler that is called when a `StackItem` is added to the undo- or the redo-stack.

**`undoManager.on('stack-item-popped', function({stackItem, origin, type:'undo'|'redo', changedParentTypes}, undoManager))`**\
Register an event handler that is called when a `StackItem` is popped from the undo- or the redo-stack.

**`undoManager.on('stack-item-updated', function({stackItem, origin, type:'undo'|'redo', changedParentTypes}, undoManager))`**\
Register an event handler that is called when a `StackItem` is updated in the undo- or the redo-stack.

### **Example: Stop Capturing**

UndoManager merges Undo-StackItems if they are created within time-gap smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next StackItem won't be merged.

```javascript
// without stopCapturing
ytext.insert(0, 'a')
ytext.insert(1, 'b')
undoManager.undo()
ytext.toString() // => '' (note that 'ab' was removed)

// with stopCapturing
ytext.insert(0, 'a')
undoManager.stopCapturing()
ytext.insert(0, 'b')
undoManager.undo()
ytext.toString() // => 'a' (note that only 'b' was removed)
```

### **Example: Specify tracked origins**

Every change on the shared document has an origin. If no origin was specified, it defaults to `null`. By specifying `trackedOrigins` you can selectively specify which changes should be tracked by `UndoManager`. The UndoManager instance is always added to `trackedOrigins`.

```javascript
class CustomBinding {}

const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext, {
  trackedOrigins: new Set([42, CustomBinding])
})

ytext.insert(0, 'abc')
undoManager.undo()
ytext.toString() // => 'abc' (does not track because origin `null` and not part
                 //           of `trackedOrigins`)
ytext.delete(0, 3) // revert change

doc.transact(() => {
  ytext.insert(0, 'abc')
}, 42)
undoManager.undo()
ytext.toString() // => '' (tracked because origin is an instance of `trackedOrigins`)

doc.transact(() => {
  ytext.insert(0, 'abc')
}, 41)
undoManager.undo()
ytext.toString() // => 'abc' (not tracked because 41 is not an instance of
                 //        `trackedOrigins`)
ytext.delete(0, 3) // revert change

doc.transact(() => {
  ytext.insert(0, 'abc')
}, new CustomBinding())
undoManager.undo()
ytext.toString() // => '' (tracked because origin is a `CustomBinding` and
                 //        `CustomBinding` is in `trackedOrigins`)
```

### **Example: Add additional information to the StackItems**

When undoing or redoing a previous action, it is often expected to restore additional meta information like the cursor location or the view on the document. You can assign meta-information to Undo-/Redo-StackItems.

```javascript
const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext, {
  trackedOrigins: new Set([42, CustomBinding])
})

undoManager.on('stack-item-added', event => {
  // save the current cursor location on the stack-item
  event.stackItem.meta.set('cursor-location', getRelativeCursorLocation())
})

undoManager.on('stack-item-popped', event => {
  // restore the current cursor location on the stack-item
  restoreCursorLocation(event.stackItem.meta.get('cursor-location'))
})
```


# Y.Event

### Y.Event API

**`yevent.target: Y.AbstractType`**\
&#x20;   The shared type that this event was created on. This event describes the changes on `target`.

**`yevent.currentTarget: Y.AbstractType`**\
&#x20;   The current target of the event as the event traverses through the (deep)observer callbacks. It refers to the type on which the event handler (observe/observeDeep) has been attached. Similar to [Event.currentTarget](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget).

**`yevent.transaction: Y.Transaction`**\
&#x20;   The transaction in which this event was created on.

**`yevent.path: Array<String|number>`** \
&#x20;   Computes the path from the Y.Doc to the changed type. You can traverse to the changed type by calling `ydoc.get(path[0]).get(path[1]).get(path[2]).get( ..`.

**`yevent.changes.delta: Delta`**\
&#x20;   Computes the changes in the array-delta format. See more in the [Delta Format ](/api/delta-format)section. The text delta is only available on Y.TextEvent (`ytextEvent.delta`)

**`yevent.changes.keys: Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>`**\
&#x20;   Computes changes on the attributes / key-value map of a shared type. In Y.Map it is used to represent  changed keys. In Y.Xml it is used to describe changes on the XML-attributes.


# Delta Format

The [Delta Format](https://quilljs.com/docs/delta/) was originally described by the Quill rich text editor. We adapted the approach to describe changes on sequence-like data (e.g. Y.Text, Y.Array, Y.XmlFragment).

A Delta is a format to describe changes on a sequence-like data structure like Y.Array, Y.XmlFragment, or Y.Text. But it can also be used to describe the current state of a sequence-like data structure, as you can see in the following example:

```javascript
const ytext = ydoc.getText()

ytext.toDelta() // => []
ytext.insert(0, 'World', { bold: true })
ytext.insert(0, 'Hello ')
ytext.toDelta() // => [{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }]
```

In many cases, you can even use the delta format to apply changes on a document. In Y.Text the delta format is very handy to express complex operations:

```javascript
ytext.insert(0, 'Hello ')
ytext.insert(6, 'World', { bold: true })
// is equivalent to 
ytext.applyDelta([{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }])
```

### Delta Description

#### Delete

```javascript
delta = [{
  delete: 3
}]
```

Expresses the intention to delete the first 3 characters.

#### Retain

```javascript
delta = [{
  retain: 1
}, {
  delete: 3
}]
```

Expresses the intention to retain one item, and then delete three. E.g.

```javascript
ytext.insert(0, '12345')
ytext.applyDelta(delta)
ytext.toDelta() // => { insert: '15' }
```

#### Insert (on Y.Text)

The `insert` delta is always a string in Y.Text. Furthermore, Y.Text also allows assigning formatting attributes to the inserted text.

```javascript
delta = [{
  retain: 1
}, {
  insert: 'abc', attributes: { bold: true }
}, {
  retain: 1
}, {
  insert: 'xyz'
}]
```

Expresses the intention to insert `"abc"` at index-position 1 and make the insertion bold. Then we skip another character and insert `"xyz"` without formatting attributes. E.g.

```javascript
ytext.insert(0, '123')
ytext.applyDelta(delta)
ytext.toDelta() // => [{ insert: '1' },
                //     { insert: 'abc', attributes: { bold: true } },
                //     { insert: '2xyz3' }]
```

#### Retain (on Y.Text)

In Y.Text the `retain` delta may also contain formatting attributes that are applied on the retained text.

```javascript
delta = [{
  retain: 5, attributes: { italic: true }
}]
```

Expresses the intention to format the first five characters italic.

#### Insert (on Y.Array & Y.XmlFragment)

The `insert` delta is always an Array of inserted content in Y.Array & Y.XmlFragment.

```javascript
yarray.observe(event => { console.log(event.changes.delta) })

yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }]
yarray.insert(2, ["abc"]) // => [{ retain: 2 }, { insert: ["abc"] }]
yarray.delete(0, 1) // => [{ delete: 1 }]
```

The delta format is very powerful to express changes that are performed in a Transaction. As explained in the [shared types section](/getting-started/working-with-shared-types#transactions), events are fired after transactions. With the delta format we can express multiple changes in a single event. E.g.

```javascript
yarray.observe(event => { console.log(event.changes.delta) })

ydoc.transact(() => {
  // perform all changes in a single transaction
  yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }]
  yarray.insert(2, ["abc"]) // => [{ retain: 2 }, { insert: ["abc"] }]
  yarray.delete(0, 1) // => [{ delete: 1 }]
}) // => [{ insert: [2, "abc", 3] }]

ydoc.transact(() => {
  yarray.insert(0, ['x'])
  yarray.insert(2, ['y'])
}) // => [{ insert: ['x'] }, { retain: 1 }, { insert: ['y'] }]
```


# Document Updates

How to sync documents with other peers.

Changes on the shared document are encoded into binary encoded (highly compressed) *document updates*. Document updates are *commutative, associative,* and *idempotent*. This means that you can apply them in any order and multiple times. All clients will sync up when they received all document updates.

## Update API

**`Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])`**\
Apply a document update on the shared document. Optionally you can specify `transactionOrigin` that will be stored on `transaction.origin` and `ydoc.on('update', (update, origin) => ..)`.

**`Y.encodeStateAsUpdate(Y.Doc, [encodedTargetStateVector:Uint8Array]): Uint8Array`**\
Encode the document state as a single update message that can be applied on the remote document. Optionally, specify the target state vector to only write the missing differences to the update message.

**`Y.encodeStateVector(Y.Doc): Uint8Array`**\
Computes the state vector and encodes it into an Uint8Array. A state vector describes the state of the local client. The remote client can use this to exchange only the missing differences.

**`ydoc.on('update', eventHandler: function(update: Uint8Array, origin: any, doc: Y.Doc))`**\
Listen to incremental updates on the Yjs document. This is part of the [Y.Doc API](/api/y.doc#event-handler). Send the computed incremental update to all connected clients, or store it in a database.

**`Y.logUpdate(Uint8Array)`** (experimental)\
Log the contents of a document update to the console. This utility function is only meant for debugging and understanding the Yjs document format. It is marked as experimental because it might be changed or removed at any time.

## Alternative Update API

It is possible to sync clients and compute delta updates without loading the Yjs document to memory. Yjs exposes an API to compute the differences directly on the binary document updates. This allows you to sync efficiently while only maintaining the compressed binary-encoded document state in-memory. [(see example)](#example-syncing-clients-without-loading-the-y.doc)

Note that this feature only merges document updates and doesn't garbage-collect deleted content. You still need to load the document to a Y.Doc to reduce the document size.

**`Y.mergeUpdates(Array<Uint8Array>): Uint8Array`**\
Merge several document updates into a single document update while removing duplicate information. The merged document update is always smaller than the separate updates because of the compressed encoding.

**`Y.encodeStateVectorFromUpdate(Uint8Array): Uint8Array`**\
Computes the state vector from a document update and encodes it into an Uint8Array.

**`Y.diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array`**\
Encode the missing differences to another update message. This function works similarly to `Y.encodeStateAsUpdate(ydoc, stateVector)` but works on updates instead.

## Examples

### **Example: Listen to update events and apply them on a remote client**

```javascript
const doc1 = new Y.Doc()
const doc2 = new Y.Doc()

doc1.on('update', update => {
  Y.applyUpdate(doc2, update)
})

doc2.on('update', update => {
  Y.applyUpdate(doc1, update)
})

// All changes are also applied to the other document
doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?'])
doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?'
```

You can also use a transaction to specify an origin. This may help to eliminate redundant packets from hitting the wire.

```javascript
doc1.on('update', (update, origin) => {
  if (origin !== 'doc1') {
    return
  }
  Y.applyUpdate(doc2, update)
})

doc2.on('update', (update, origin) => {
  if (origin !== 'doc2') {
    return
  }
  Y.applyUpdate(doc1, update)
})

doc1.transact( ()=> {
  doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?'])
}, 'doc1')
```

### Syncing clients

Yjs internally maintains a [state vector](https://github.com/yjs/yjs#State-Vector) that denotes the next expected clock from each client. In a different interpretation, it holds the number of modifications created by each client. When two clients sync, you can either exchange the complete document structure or only the differences by sending the state vector to compute the differences.

#### **Example: Sync two clients by exchanging the complete document structure**

```javascript
const state1 = Y.encodeStateAsUpdate(ydoc1)
const state2 = Y.encodeStateAsUpdate(ydoc2)
Y.applyUpdate(ydoc1, state2)
Y.applyUpdate(ydoc2, state1)
```

#### **Example: Sync two clients by computing the differences**

This example shows how to sync two clients with a minimal amount of data exchanged by computing the differences using the state vector of the remote client. Syncing clients using the state vector requires another roundtrip but can save a lot of bandwidth.

```javascript
const stateVector1 = Y.encodeStateVector(ydoc1)
const stateVector2 = Y.encodeStateVector(ydoc2)
const diff1 = Y.encodeStateAsUpdate(ydoc1, stateVector2)
const diff2 = Y.encodeStateAsUpdate(ydoc2, stateVector1)
Y.applyUpdate(ydoc1, diff2)
Y.applyUpdate(ydoc2, diff1)
```

#### Example: Syncing clients without loading the Y.Doc

```javascript
// encode the current state as a binary buffer
let currentState1 = Y.encodeStateAsUpdate(ydoc1)
let currentState2 = Y.encodeStateAsUpdate(ydoc2)
// now we can continue syncing clients without
// using the Y.Doc
ydoc1.destroy()
ydoc2.destroy()

const stateVector1 = Y.encodeStateVectorFromUpdate(currentState1)
const stateVector2 = Y.encodeStateVectorFromUpdate(currentState2)
const diff1 = Y.diffUpdate(currentState1, stateVector2)
const diff2 = Y.diffUpdate(currentState2, stateVector1)

// sync clients
currentState1 = Y.mergeUpdates([currentState1, diff2])
currentState2 = Y.mergeUpdates([currentState2, diff1])
```

### Example: Base64 encoding

We compress document updates to a highly compressed binary format. Therefore, document updates are represented as [Uint8Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). An `Uint8Array` represents binary data similarly to a [NodeJS' Buffer](https://nodejs.org/api/buffer.html) . The difference is that `Uint8Array` is available in all JavaScript environments. The catch is that you can't [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)/[`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) the data because there is no JSON representation for binary data. However, most communication protocols support binary data. If you still need to transform the data into a string, you can use [Base64 encoding](https://en.wikipedia.org/wiki/Base64). For example, by using the [`js-base64`](https://www.npmjs.com/package/js-base64) library:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import { fromUint8Array, toUint8Array } from 'js-base64'

const documentState = Y.encodeStateAsUpdate(ydoc) // is a Uint8Array
// Transform Uint8Array to a Base64-String
const base64Encoded = fromUint8Array(documentState)
// Transform Base64-String back to an Uint8Array
const binaryEncoded = toUint8Array(base64Encoded)
```

{% endtab %}

{% tab title="Install" %}

```bash
npm install js-base64
```

{% endtab %}
{% endtabs %}

### Example: Building a custom provider

A "provider" is what connects a Yjs document to other clients (through a network) or that synchronizes a document with a database. The section [syncing clients](#syncing-clients) explains several concepts to sync a Yjs document with another client or server. Once the initial states are synchronized, we want to synchronize incremental updates by listening to the update and forwarding them to the other clients. We can use the concept of *transaction origin* to determine whether we need to forward a document update to the database/network. I recommend using the following template for custom provider implementation.

```javascript
import * as Y from 'yjs'
import { Observable } from 'lib0/observable'

class Provider extends Observable {
  /**
   * @param {Y.Doc} ydoc
   */
  constructor (ydoc) {
    super()

    ydoc.on('update', (update, origin) => {
      // ignore updates applied by this provider
      if (origin !== this) {
        // this update was produced either locally or by another provider. 
        this.emit('update', [update])
      }
    })
    // listen to an event that fires when a remote update is received
    this.on('update', update => {
      Y.applyUpdate(ydoc, update, this) // the third parameter sets the transaction-origin
    })
  }
  ..
}
```

Note that this is not the only way to filter updates. You could also use a `isLocal` flag or use a [lib0/mutex](https://github.com/dmonad/lib0). However, it is recommended that all providers set the transaction origin which makes it easier for developers to debug where an update comes from.


# Y.RelativePosition

A powerful position encoding that transforms back to index positions

When working with collaborative documents, we often need to work with positions. Positions may represent cursor locations, selection ranges, or even assign a comment to a range of text. Normal index-positions (expressed as integers) are not convenient to use because the index-range is invalidated as soon as a remote change manipulates the document. Relative positions give you a powerful API to express positions.

A *relative position* is fixated to an element in the shared document and is not affected by remote changes. I.e. given the document `"a|c"`, the relative position is attached to `c`. When a remote user modifies the document by inserting a character before the cursor, the cursor will stay attached to the character `c`. `insert(1, 'x')("a|c") = "ax|c"`. When the *relative position* is set to the end of the document, it will stay attached to the end of the document.

{% hint style="info" %}
Relative positions are guaranteed to always point to the same location ⇒ When all clients sync up, all relative positions will translate to the same index-position. This is not possible in OT-like solutions [(explanation)](https://marijnhaverbeke.nl/blog/collaborative-editing-cm.html).
{% endhint %}

## Y.RelativePosition API

**`Y.createRelativePositionFromTypeIndex(type: Y.AbstractType, index: number [, assoc=0]): Y.RelativePosition`**\
&#x20;   Create a relative position fixated to the i-th element in any sequence-like shared type (if `assoc >= 0`). By default, the position associates with the character that comes after the specified index position. If `assoc < 0`, then the relative position associates with the character before the specified index position.&#x20;

**`Y.createAbsolutePositionFromRelativePosition(Y.RelativePosition, Y.Doc): { type: Y.AbstractType, index: number, assoc: number } | null`**\
&#x20;   Create an absolute position from a relative position. If the relative position cannot be referenced, or the type is deleted, then the result is null.

**`Y.encodeRelativePosition(Y.RelativePosition): Uint8Array`**\
&#x20;   Encode a relative position to an Uint8Array. Binary data is the preferred encoding format for [document updates](/api/document-updates). If you prefer JSON encoding, you can simply `JSON.stringify` / `JSON.parse` the relative position instead.

**`Y.decodeRelativePosition(Uint8Array): RelativePosition`**\
&#x20;   Decode a binary-encoded relative position to a RelativePositon object.

### **Example: Transform to RelativePosition and back**

```javascript
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const pos = Y.createAbsolutePositionFromRelativePosition(relPos, doc)
pos.type === ytext // => true
pos.index === 2 // => true
```

### **Example: Send relative position to a remote client (JSON)**

```javascript
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const encodedRelPos = JSON.stringify(relPos)
// send encodedRelPos to remote client..
const parsedRelPos = JSON.parse(encodedRelPos)
const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
pos.type === remoteytext // => true
pos.index === 2 // => true
```

### **Example: Send relative position to a remote client (Uint8Array)**

```javascript
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const encodedRelPos = Y.encodeRelativePosition(relPos)
// send encodedRelPos to remote client..
const parsedRelPos = Y.decodeRelativePosition(encodedRelPos)
const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
pos.type === remoteytext // => true
pos.index === 2 // => true
```


# Awareness

API documentation of the Awareness CRDT

Awareness is an optional feature that works well together with Yjs. As I described in [Awareness & Presence](/getting-started/adding-awareness), the feature is not part of the `yjs` module. It is defined in [y-protocols](https://github.com/yjs/y-protocols) and is usually implemented by the providers. If you want to implement your custom provider, you can use the Awareness CRDT, or implement a custom protocol. It's up to you.

If you haven't already, you should read the getting-started guide on awareness.

{% content-ref url="/pages/-MArc8idEI4LQq6hMSFQ" %}
[Awareness & Presence](/getting-started/adding-awareness)
{% endcontent-ref %}

## Awareness CRDT

```javascript
import * as awarenessProtocol from 'y-protocols/awareness.js'
```

The awareness protocol implements a simple network agnostic CRDT that manages user status (who is online?) and propagate awareness information like cursor location, username, or email address. Each client can update its own local state and listen to state changes of remote clients.

Each client has an awareness state. Remote awareness states are stored in a Map that maps from remote client id to an awareness state. An *awareness state* is an increasing clock attached to a schemaless JSON object.

Whenever the client changes its local state, it increases the clock and propagates its own awareness state to all peers. When a client receives a remote awareness state and overwrites the client's state if the received state is newer than the local state of that client. If the state is `null`, the client is marked as offline. If a client doesn't receive updates from a remote peer for 30 seconds, it marks the remote client as offline. Hence each client must broadcast its own awareness state in a regular interval to make sure that remote clients don't mark it as offline.

### **Awareness CRDT API**

```javascript
awareness = new awarenessProtocol.Awareness(ydoc)

// It is usually created & maintained by the provider
awareness = provider.awareness
```

**`awareness = new awarenessProtocol.Awareness(ydoc: Y.Doc)`**\
Create a new awareness instance.

**`awareness.clientID: number`**\
A unique identifier that identifies this client.

**`awareness.destroy()`**\
Destroy the awareness instance and all associated state and event-handlers.

**`awareness.getLocalState(): Object<string, any> | null`**\
Get the local awareness state.

**`awareness.setLocalState(state: Object<string, any> | null)`**\
Set or update the local awareness state. Set `null` to mark the local client as offline. The `state` object must be a key-value store that maps to JSON-encodable values.

**`awareness.setLocalStateField(string, any)`**\
Only set or update a single key-value pair in the local awareness state.

**`awareness.getStates(): Map<number, Object<string, any>>`**\
Get all awareness states (remote and local). Maps from `clientID` to awareness state. The clientID is usually the `ydoc.clientID`.

**`awareness.on('update', ({ added: Array<number>, updated: Array<number>, removed: Array<number> }, [transactionOrigin:any]) => ..)`**\
Listen to remote and local awareness changes. This event is called even when the awareness state does not change but is only updated to notify other users that this client is still online. Use this event if you want to propagate awareness state to other users.

**`awareness.on('change', ({ added: Array<number>, updated: Array<number>, removed: Array<number> }, [transactionOrigin:any]) => ..)`**\
Listen to remote and local state changes. Get notified when a state is either added, updated, or removed.

## Awareness Protocol

The awareness protocol is implemented by most providers. It allows you to use the Awareness CRDT to propagate presence and awareness information. Although it is not a requirement, it is recommended that all providers that interact with Yjs implement this protocol. If you want to implement the awareness protocol into your custom provider, this section is for you.

### Awareness Protocol API

**`awarenessProtocol.encodeAwarenessUpdate(awareness: Awareness, clients: Array<number>): Uint8Array`**\
Encode the awareness states of the specified clients into an update encoded as `Uint8Array`.

**`awarenessProtocol.applyAwarenessUpdate(awareness: Awareness, update: Uint8array, origin: any)`**\
Apply an awareness update created with `encodeAwarenessUpdate` to an instance of the Awareness CRDT.

**`awarenessProtocol.removeAwarenessStates(awareness: Awareness, clients: Array<number>, origin: any)`**\
Remove the awareness states of the specified clients. This will call the `update` and the `change` event handler of the Awareness CRDT. Sometimes you want to mark yourself or others as offline. As soon as you know that a client is offline, you should call this function. It is not part of the Awareness CRDT, because it should only be used by the provider that implements awareness.

### Adding Awareness Support to a Provider

Awareness CRDT updates work similarly to Yjs updates. First, you sync with a client. Then you exchange incremental updates with that client using the `update` event. The only difference is that the Awareness CRDT doesn't support *state vectors* to exchange a minimal amount of information. That only makes things easier and has little performance impact because awareness states are usually pretty small.

```javascript
// Encode the complete Awareness state
const encodedAwState = awarenessProtocol.encodeAwarenessUpdate(
  provider.awareness,
  Array.from(provider.awareness.getStates().keys())
)
// Make sure to share the complete awareness state whenever you
// connect to a new client. Similarly to the Yjs CRDT, it doesn't
// matter if the remote client receives the same state-updates several
// times. What is important is that the state is distributed.

// Whenever the local state changes, communicate that change to all connected clients
awareness.on('update', ({ added, updated, removed }) => {
  const changedClients = added.concat(updated).concat(removed)
  broadcastAwarenessMessage(awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients))
})
```

That's basically it. Here are just a few small tricks that I like to implement in providers:

When you know that a client will disconnect, you might as well send a small update to other users to let them know that you went offline. It is not strictly necessary, because the Awareness CRDT will notice that you are offline after a timeout. But you can at least try:

```javascript
window.addEventListener('beforeunload', () => {
  awarenessProtocol.removeAwarenessStates(
    this.awareness, [doc.clientID], 'window unload'
  )
})
```

When your connection already broke up, you should mark all remote clients as offline. That just makes sense to the user that is using your software.

```javascript
websocket.onclose = () => {
  // mark everyone but the current client as offline
  awarenessProtocol.removeAwarenessStates(
    provider.awareness,
    Array.from(provider.awareness.getStates().keys())
      .filter(client => client !== provider.doc.clientID),
    'connection closed'
  )
}
```

When you get lost, you should have a look at one of the existing providers that implement the awareness protocol. The y-websocket is a fairly simple provider and might be a good starting point for you.

{% embed url="<https://github.com/yjs/y-websocket/>" %}


# Subdocuments

Embedding Yjs documents into Yjs documents

Yjs documents can be embedded into shared types. This allows you to manage vast amounts of Yjs documents as part of a root document.

```javascript
// Client One
const rootDoc = new Y.Doc()
const folder = rootDoc.getMap()

const subDoc = new Y.Doc()
subDoc.getText().insert(0, 'some initial content')
folder.set('my-document.txt', subDoc)
```

An obvious use-case is to manage documents in a folder structure. Each document (potentially containing large amounts of rich-text content) could be represented as a subdocument that is lazily loaded to memory when needed. By default, subdocuments are empty until they are explicitly loaded.

```javascript
// Client Two
const subDoc = rootDoc.getMap().get('my-document.txt')
const subDocText = subDoc.getText()
subDocText.toString() // => "" - content is empty

// Data needs to be loaded first..
subDoc.load()

// Then the providers will fetch data from the database / network
// and eventually fill the content
subDoc.on('synced', () => {
  subDocText.toString() // => "some initial content"
})

// It is hard to determine when data was actually synced.
// The synced event is still helpful to show the user that this user synced
// with other users.
// It is safer to observe the data types and don't listen
// to an explicit sync event.
subDocText.observe(() => {
  // data changed..
})
```

Subdocuments are lazily loaded after they have been explicitly loaded to memory. A subdocument can be destroyed `doc.destroy()` to free all used memory and destroy existing data bindings. The document can be accessed again to force the provider to load the content again.

```javascript
const subDoc = rootDoc.getMap().get('my-document.text')
subDoc.load()

// After some time you might not need to render the document anymore.
// You want to destroy the existing data bindings and load a different document
...

subDoc.destroy() // free all memory and destroy all data bindings to this document.

// You should not access the destroyed document again.
// Instead you can create a new one by requesting the document again.
const subDocReloaded = rootDoc.getMap().get('my-document.text')
subDocReloaded.load()
```

It is up to the providers to sync subdocuments. It is possible to create a very efficient sync mechanism using sub-documents. The providers can sync collections of documents in one flush instead of having multiple requests. It is also possible to handle authorization over the folder structure. But all official Yjs providers currently think of sub-documents as separate entities. A new feature that was introduced in Yjs\@13.4.0 is that all documents are given a GUID. The documents are identified with GUIDs and used as a room-name to sync documents. This allows you to duplicate data in the document structure.

```javascript
const rootDoc = new Y.Doc()

const doc = new Y.Doc()
doc.guid // => "123e4567-e89b-12d3-a456-426614174000" - A random UUIDv4

rootDoc.getMap().set('file.txt', doc)

// we can include a copy of a subdocument by giving it the same UUIDv4
const copy = new Y.Doc({ guid: doc.guid })
rootDoc.getMap().set('copy.txt', copy)

// `doc` and `copy` will automatically sync because they have the same guid
```

By default, all subdocuments must be explicitly loaded before they are filled with content. It is possible to define `Y.Doc({ autoLoad: true })` to specify that all peers should automatically load the document.

Providers listen to `subdocs` events to get notified when subdocuments are added, removed, or loaded.

```typescript
doc.on('subdocs', ({ added: Set<Y.Doc>, removed: Set<Y.Doc>, loaded: Set<Y.Doc> }) => {
  // use added / removed to sync documents in the background
  // use loaded to fill document content
})
```

Providers (e.g. y-websocket, y-indexeddb) are responsible for syncing subdocuments. Not all providers support subdocuments yet. A simple method to implement lazy-loading documents is to create a provider instance to the `doc.guid`-room once a document is loaded:

```javascript
doc.on('subdocs', ({ loaded }) => {
  loaded.forEach(subdoc => {
    new WebrtcProvider(subdoc.guid, subdoc)
  })
})
doc.getSubdocs() // Get the Set<Y.Doc> of all subdocuments
```

\\


# Internals

Implementation Details - The inner working of Yjs

### CRDT Paper

Yjs is a CRDT implementation. It implements an adaptation of the YATA CRDT with improved runtime performance.

{% embed url="<https://www.researchgate.net/publication/310212186_Near_Real-Time_Peer-to-Peer_Shared_Editing_on_Extensible_Data_Types>" %}

### Implementation Details

Choosing efficient data structures is critical when implementing a CRDT. The following document gives an overview of the data structures used in Yjs.

{% embed url="<https://github.com/yjs/yjs/blob/main/INTERNALS.md>" %}

### Internals Visualization

Visualization of different CRDT algorithms (including Yjs/YATA and Automerge/RGA).

{% embed url="<https://text-crdt-compare.surge.sh/>" %}

### Optimizations Overview

JavaScript manages memory automatically using a garbage collection approach. Yjs is a particularly efficient implementation of the YATA CRDT that works well in the browser and in NodeJS. This article analyzes the performance of Yjs in JavaScript.&#x20;

{% embed url="<https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing>" %}

### Codebase Walkthrough

{% embed url="<https://youtu.be/0l5XgnQ6rB4>" %}


# FAQ

### I get a new ClientID for every session, is there a way to make it static for a peer accessing the document?

The ClientID is used for conflict resolution. So it is important that you understand all side-effects of retaining a ClientID across sessions. The simple answer is: Yjs is designed to create a new ClientID for every session to avoid sync conflicts. The recommended method to identify users is using the Awareness feature. If you still want to retain a ClientID, you can do so by simply overwriting the `ydoc.clientID` property. But you must ensure that no other `Y.Doc` instance is currently holding that ClientID. This is not always possible: A user might open several browser windows with the same user account. When two `Y.Doc` instances with the same ClientID exist, the document might get permanently corrupted without a way to recover. So do this with caution.

### Structuring data in smaller YDocs

One basically needs to decide on the following:

1. To use one or multiple YDocs for an entity or set of entities in your application.
2. How to structure the data within a YDoc.

When reasoning around how to structure data in Yjs I recommend to consider these aspects:

1. **The flow of data for common use cases:** It can be good to group data that is often used together. In contrast, it may not be practical to load hundreds of YDocs at once or load new YDocs very frequently.
2. **Read/write permissions:** Permissions cannot be practically enforced within a YDoc so you need to split data into multiple YDocs if you need different permissions for different parts of the data.
3. **Size is very rarely a practical problem** as long as you deal with human-entered text input. (See [benchmarks](https://github.com/dmonad/crdt-benchmarks).)
4. **Separate structure and data:** In some cases, it can be practical to have one YDoc that holds only the id references across entities (eg. pages) and one YDoc per entity data. This is particularly relevant if you need different permission levels for different entities. If you have no need for granular control, a split like this may be unnecessarily complex.
5. **History and undo:** At what level is it natural to track edit history and perform undo? It is much easier to perform history tracking within a single YDoc rather than spread across multiple YDocs.
6. **Consider using a single top-level YMap:** Top-level shared types cannot be deleted, so you may want to structure all your data in a single top-level YMap, eg. `yDoc.getMap('data').get('page-1')`.
7. **Subdocuments:** You may also consider using [subdocuments 2](https://docs.yjs.dev/api/subdocuments). However, it gets a bit more complex and your provider may not support it.


# Meshing Providers

## Work in Progress - ***come back later***


# Persisting the Document to a Central Database

## Work in Progress - ***come back later***


# Indefinite Scaling with y-redis

## Work in Progress - ***come back later***


# Lessons Learned

## Work in Progress - ***come back later***


# Custom Provider

How to sync document updates to a database or network

You already learned that document updates in Yjs are commutative, associative, and idempotent. Yjs doesn't care in which order document updates are applied, as long as all changes are applied eventually. It is the role of the provider to distribute or store updates generated by the Yjs document. A network provider sync document updates to other peers. A database provider syncs document updates to a database. In this tutorial, you will learn how to build your custom provider for Yjs. The [ Document Updates](/api/document-updates) section is a prerequisite for this tutorial.

\[\[todo]]

Example: The simplest database provider

Example: The simples network provider

Recommendaditions

*

## TODO

* What a provider does
* Find a new analogy of blocks
* Network provider - y-protocols
* Database provider - tricks how to merge updates (after differential updates feature is finished)
* How to sync subdocuments
*


# Talks, Podcasts, and Blogs

Yjs related resources in chronological order

{% hint style="info" %}
If you know of any resources related to Yjs, please post them to the [discussion board](https://discuss.yjs.dev/).
{% endhint %}

## September 26, 2020 - Joseph Gentle, CRDTs are the future

{% embed url="<https://josephg.com/blog/crdts-are-the-future/>" %}

## September 24, 2020 - Microsoft Ignite, The future of data systems

{% embed url="<https://myignite.microsoft.com/sessions/7db762c9-2629-4cff-849f-0e043eb12cef>" %}

## September 3, 2020 - Yjs walkthrough with Joseph Gentle & Kevin Jahns

{% embed url="<https://youtu.be/0l5XgnQ6rB4>" %}

## August 10, 2020 - Blog post by Kevin Jahns

{% embed url="<https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/>" %}

## April 8, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-webrtc-part-5>" %}

## March 30, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-webrtc-part-4>" %}

## March 24, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/gutenberg-part-4>" %}

## March 19, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-webrtc-part-3>" %}

## March 17, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/gutenberg-part-3>" %}

## March 17, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/signal-y-webrtc-part2>" %}

## March 10, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/gutenberg-part-2>" %}

## March 5, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-webrtc-part-1>" %}

## March 3, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/shared-editing-wordpress1>" %}

## February 19, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-deep-dive-part-4>" %}

## February 19, 2020 - Podcast by Tag1 Consulting & Preston So

{% embed url="<https://youtu.be/oMJgXopewc4>" %}

## February 13, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-deep-dive-part-3>" %}

## February 6, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-deep-dive-part-2>" %}

## February 5, 2020 - Podcast by Tag1 Consulting

{% embed url="<https://www.tag1consulting.com/blog/yjs-indexeddb-TTT-009>" %}

## February 2, 2020 - Talk at FOSDEM

{% embed url="<https://fosdem.org/2020/schedule/event/yjs_shared_editing/>" %}

{% embed url="<https://video.fosdem.org/2020/H.2215/yjs_shared_editing.webm>" %}

## January 30, 2020 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/yjs-deep-dive-part-1>" %}

## January 22, 2020 - Podcast by Tag1 Consulting

{% embed url="<https://www.tag1consulting.com/blog/peer-peer-collaborative-editing-using-yjs-webrtc-tag1-team-talk-007>" %}

## November 21, 2019 - Interview by Publishpress

{% embed url="<https://publishpress.com/blog/yjs/>" %}

{% embed url="<https://youtu.be/oR7l_v7B6mg>" %}

## November 13, 2019 - Podcast by Tag1 Consulting

{% embed url="<https://www.tag1consulting.com/blog/deep-dive-yjs-part-2-tag1-team-talk-005>" %}

## November 12, 2019 - Podcast by Tag1 Consulting

{% embed url="<https://www.tag1consulting.com/blog/deep-dive-yjs-part-1-tag1-team-talk-004>" %}

## November 11, 2019 - Blog post by Tag1 Consulting & Preston So

{% embed url="<https://www.tag1consulting.com/blog/evaluating-real-time-collaborative-editing-solutions-top-fortune-50-company>" %}

## September 18, 2019 - Podcast by Tag1 Consulting

{% embed url="<https://www.tag1consulting.com/blog/deep-dive-real-time-collaborative-editing-solutions-tagteamtalk-001-0>" %}


