Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2486340
feat: de-couple yjs from blocknote/core
nperez0111 May 13, 2026
a437d1c
fix: proper position tracking for uninitialized doc #2759
nperez0111 May 20, 2026
fbb628e
fix: setup for yjs 14
nperez0111 May 14, 2026
053c9a5
feat: allow user colors on suggestion marks
nperez0111 May 13, 2026
221ad3c
feat: working `@y/prosemirror` demo
nperez0111 May 15, 2026
da657a4
fix: have `@blocknote/core/y` implement `RelativePositionMappingExten…
nperez0111 May 18, 2026
39551bc
fix: slightly better working fork doc plugin
nperez0111 May 18, 2026
571b4f3
feat: more progress into the version history demo
nperez0111 May 19, 2026
59489e9
test: get tests passing again
nperez0111 May 19, 2026
609763d
chore: attrs?
nperez0111 May 19, 2026
9673246
chore: pnpm-lock
nperez0111 May 20, 2026
ca9dd8f
chore: mark `@y/y` as optional
nperez0111 May 21, 2026
3b13bdb
test: copy over RelativePositionMapping tests to @y/y and fix positio…
nperez0111 May 21, 2026
6c3a177
feat: add versioning extension, Yjs thread store, and versioning exam…
nperez0111 May 22, 2026
9acc555
fix: widen forEachAttr callback key type to match @y/y's YType signature
nperez0111 May 22, 2026
89669d2
chore: update excludes & mapAttributionToMark
nperez0111 May 26, 2026
3d29abd
fix: make table content support attribution marks
nperez0111 May 26, 2026
9ea564c
basic tests
YousefED May 28, 2026
0ad6fc9
basic tests
YousefED May 28, 2026
9fae8f9
propchange tests
YousefED May 28, 2026
395f7ee
block type change tests
YousefED May 28, 2026
800735c
chore: update `@y/prosemirror` patch
nperez0111 May 28, 2026
1dac678
fix: move modification styles from xl-ai to core
nperez0111 May 29, 2026
d1697e4
chore: update to latest @y/prosemirror patch
nperez0111 May 29, 2026
3498865
Merge branch 'y-prosemirror-v14' into y-prosemirror-tests
nperez0111 May 29, 2026
b3fad88
chore: incldue built deps
nperez0111 May 29, 2026
0d0319a
Merge branch 'y-prosemirror-v14' into y-prosemirror-tests
nperez0111 May 29, 2026
13e3599
update tests
YousefED Jun 2, 2026
a5cee50
add more test cases
YousefED Jun 2, 2026
9763a42
fix types
YousefED Jun 2, 2026
6753eb1
Merge branch 'y-prosemirror-tests' of github.com:TypeCellOS/BlockNote…
YousefED Jun 2, 2026
c738388
build: patch in the latest changes for y-prosemirror
nperez0111 Jun 5, 2026
ce2c01c
Merge branch 'y-prosemirror-v14' into y-prosemirror-tests
nperez0111 Jun 5, 2026
a27e0f9
test: update y-prosemirror suggestion snapshots for trailing-block re…
nperez0111 Jun 5, 2026
162edfc
test: render YDoc marks + attribution in suggestion snapshots
nperez0111 Jun 5, 2026
9eb7480
test: load @blocknote/core/style.css from source in browser tests
nperez0111 Jun 5, 2026
6694dce
test: regenerate browser screenshots with source CSS + raise stable-c…
nperez0111 Jun 5, 2026
0ccde78
docs: correct outdated y-attributed-format comment in basicText test
nperez0111 Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 24 additions & 22 deletions docs/content/docs/features/collaboration/comments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,28 @@ To enable comments in your editor, you need to:
- Optionally provide a schema for comments and comment editors to use. If left undefined, they will use the [default comment editor schema](https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/Comments/defaultCommentEditorSchema.ts). See [here](/docs/features/custom-schemas) to find out more about custom schemas.

```tsx
const editor = useCreateBlockNote({
extensions: [
CommentsExtension({
// See below.
threadStore: ...,
// Return user information for the given userIds (see below).
resolveUsers: async (userIds: string[]) => { ... },
// Optional, can be left undefined
schema: BlockNoteSchema.create(...)
}),
import { withCollaboration } from "@blocknote/core/yjs";

const editor = useCreateBlockNote(
withCollaboration({
extensions: [
CommentsExtension({
// See below.
threadStore: ...,
// Return user information for the given userIds (see below).
resolveUsers: async (userIds: string[]) => { ... },
// Optional, can be left undefined
schema: BlockNoteSchema.create(...)
}),
...
],
collaboration: {
// See real-time collaboration docs
...
},
...
],
collaboration: {
// See real-time collaboration docs
...
},
...
});
}),
);
```

**Demo**
Expand All @@ -50,7 +54,7 @@ BlockNote comes with several built-in ThreadStore implementations:
The `YjsThreadStore` provides direct Yjs-based storage for comments, storing thread data directly in the Yjs document. This implementation is ideal for simple collaborative setups where all users have write access to the document.

```tsx
import { YjsThreadStore } from "@blocknote/core/comments";
import { YjsThreadStore } from "@blocknote/core/yjs";

const threadStore = new YjsThreadStore(
userId, // The active user's ID
Expand All @@ -68,10 +72,8 @@ The `RESTYjsThreadStore` combines Yjs storage with a REST API backend, providing
In this implementation, data is written to the Yjs document via a REST API which can handle access control. Data is still retrieved from the Yjs document directly (after it's been updated by the REST API), this way all comment information automatically syncs between clients using the existing collaboration provider.

```tsx
import {
RESTYjsThreadStore,
DefaultThreadStoreAuth,
} from "@blocknote/core/comments";
import { DefaultThreadStoreAuth } from "@blocknote/core/comments";
import { RESTYjsThreadStore } from "@blocknote/core/yjs";

const threadStore = new RESTYjsThreadStore(
"https://api.example.com/comments", // Base URL for the REST API
Expand Down
41 changes: 23 additions & 18 deletions docs/content/docs/features/collaboration/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,41 @@ Let's see how you can add Multiplayer capabilities to your BlockNote setup, and

_Try the live demo on the [homepage](https://www.blocknotejs.org)_

BlockNote uses [Yjs](https://github.com/yjs/yjs) for this, and you can set it up with the `collaboration` option:
BlockNote uses [Yjs](https://github.com/yjs/yjs) for this, and you can set it up with the `withCollaboration` helper:

```typescript
import * as Y from "yjs";
import { WebrtcProvider } from "y-webrtc";
import { withCollaboration } from "@blocknote/core/yjs";
// ...

const doc = new Y.Doc();

const provider = new WebrtcProvider("my-document-id", doc); // setup a yjs provider (explained below)
const editor = useCreateBlockNote({
// ...
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
const editor = useCreateBlockNote(
withCollaboration({
// ...
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
},
// When to show user labels on the collaboration cursor. Set by default to
// "activity" (show when the cursor moves), but can also be set to "always".
showCursorLabels: "activity",
},
// When to show user labels on the collaboration cursor. Set by default to
// "activity" (show when the cursor moves), but can also be set to "always".
showCursorLabels: "activity",
},
// ...
});
// ...
}),
);
```

The `withCollaboration` function accepts all the regular editor options along with a `collaboration` property, and configures your editor for real-time collaboration.

## Yjs Providers

When a user edits the document, an incremental change (or "update") is captured and can be shared between users of your app. You can share these updates by setting up a _Yjs Provider_. In the snipped above, we use [y-webrtc](https://github.com/yjs/y-webrtc) which shares updates over WebRTC (and BroadcastChannel), but you might be interested in different providers for production-ready use cases.
Expand Down
9 changes: 8 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,14 @@
"tailwind-merge": "^3.4.0",
"y-partykit": "^0.0.25",
"yjs": "^13.6.27",
"zod": "^4.3.5"
"zod": "^4.3.5",
"@y/protocols": "^1.0.6-rc.1",
"@y/websocket": "^4.0.0-3",
"@y/y": "^14.0.0-rc.16",
"@y/prosemirror": "^2.0.0-2",
"@floating-ui/react": "^0.27.18",
"lib0": "1.0.0-rc.13",
"y-websocket": "^2.1.0"
},
"devDependencies": {
"@blocknote/code-block": "workspace:*",
Expand Down
27 changes: 15 additions & 12 deletions examples/07-collaboration/01-partykit/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import YPartyKitProvider from "y-partykit/provider";
import * as Y from "yjs";
import { withCollaboration } from "@blocknote/core/yjs";

// Sets up Yjs document and PartyKit Yjs provider.
const doc = new Y.Doc();
Expand All @@ -15,19 +16,21 @@ const provider = new YPartyKitProvider(
);

export default function App() {
const editor = useCreateBlockNote({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
const editor = useCreateBlockNote(
withCollaboration({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
},
},
},
});
}),
);

// Renders the editor instance.
return <BlockNoteView editor={editor} />;
Expand Down
17 changes: 10 additions & 7 deletions examples/07-collaboration/03-y-sweet/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useYDoc, useYjsProvider, YDocProvider } from "@y-sweet/react";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import { withCollaboration } from "@blocknote/core/yjs";

import "@blocknote/mantine/style.css";

Expand All @@ -23,13 +24,15 @@ function Document() {
const provider = useYjsProvider();
const doc = useYDoc();

const editor = useCreateBlockNote({
collaboration: {
provider,
fragment: doc.getXmlFragment("blocknote"),
user: { color: "#ff0000", name: "My Username" },
},
});
const editor = useCreateBlockNote(
withCollaboration({
collaboration: {
provider,
fragment: doc.getXmlFragment("blocknote"),
user: { color: "#ff0000", name: "My Username" },
},
}),
);

return <BlockNoteView editor={editor} />;
}
7 changes: 4 additions & 3 deletions examples/07-collaboration/05-comments/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import {
CommentsExtension,
DefaultThreadStoreAuth,
YjsThreadStore,
} from "@blocknote/core/comments";
import { withCollaboration, YjsThreadStore } from "@blocknote/core/yjs";

import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
Expand Down Expand Up @@ -74,14 +75,14 @@ function Document() {

// setup the editor with comments and collaboration
const editor = useCreateBlockNote(
{
withCollaboration({
collaboration: {
provider,
fragment: doc.getXmlFragment("blocknote"),
user: { color: getRandomColor(), name: activeUser.username },
},
extensions: [CommentsExtension({ threadStore, resolveUsers })],
},
}),
[activeUser, threadStore],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import {
DefaultThreadStoreAuth,
YjsThreadStore,
CommentsExtension,
} from "@blocknote/core/comments";
import { withCollaboration, YjsThreadStore } from "@blocknote/core/yjs";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import {
Expand Down Expand Up @@ -77,14 +77,14 @@ export default function App() {

// setup the editor with comments and collaboration
const editor = useCreateBlockNote(
{
withCollaboration({
collaboration: {
provider,
fragment: doc.getXmlFragment("blocknote"),
user: { color: getRandomColor(), name: activeUser.username },
},
extensions: [CommentsExtension({ threadStore, resolveUsers })],
},
}),
[activeUser, threadStore],
);

Expand Down
34 changes: 19 additions & 15 deletions examples/07-collaboration/07-ghost-writer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";
import { BlockNoteView } from "@blocknote/mantine";
import { useCreateBlockNote } from "@blocknote/react";
import { withCollaboration } from "@blocknote/core/yjs";

import YPartyKitProvider from "y-partykit/provider";
import * as Y from "yjs";
Expand Down Expand Up @@ -38,21 +39,23 @@ const ghostContent =
export default function App() {
const [numGhostWriters, setNumGhostWriters] = useState(1);
const [isPaused, setIsPaused] = useState(false);
const editor = useCreateBlockNote({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: isGhostWriting
? `Ghost Writer #${ghostWriterIndex}`
: "My Username",
color: isGhostWriting ? "#CCCCCC" : "#00ff00",
const editor = useCreateBlockNote(
withCollaboration({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: isGhostWriting
? `Ghost Writer #${ghostWriterIndex}`
: "My Username",
color: isGhostWriting ? "#CCCCCC" : "#00ff00",
},
},
},
});
}),
);

useEffect(() => {
if (!isGhostWriting || isPaused) {
Expand Down Expand Up @@ -101,7 +104,8 @@ export default function App() {
`${window.location.origin}${window.location.pathname}?room=${roomName}&index=-1`,
"_blank",
);
}}>
}}
>
Ghost Writer in a new window
</button>
</>
Expand Down
29 changes: 15 additions & 14 deletions examples/07-collaboration/08-forking/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import "@blocknote/core/fonts/inter.css";
import {} from "@blocknote/core";
import { ForkYDocExtension } from "@blocknote/core/extensions";
import { ForkYDocExtension, withCollaboration } from "@blocknote/core/yjs";
import {
useCreateBlockNote,
useExtension,
Expand All @@ -21,19 +20,21 @@ const provider = new YPartyKitProvider(
);

export default function App() {
const editor = useCreateBlockNote({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
const editor = useCreateBlockNote(
withCollaboration({
collaboration: {
// The Yjs Provider responsible for transporting updates:
provider,
// Where to store BlockNote data in the Y.Doc:
fragment: doc.getXmlFragment("document-store"),
// Information (name and color) for this user:
user: {
name: "My Username",
color: "#ff0000",
},
},
},
});
}),
);
const forkYDocPlugin = useExtension(ForkYDocExtension, { editor });
const isForked = useExtensionState(ForkYDocExtension, {
editor,
Expand Down
2 changes: 1 addition & 1 deletion examples/07-collaboration/09-comments-testing/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import {
CommentsExtension,
DefaultThreadStoreAuth,
YjsThreadStore,
} from "@blocknote/core/comments";
import { YjsThreadStore } from "@blocknote/core/yjs";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
Expand Down
14 changes: 14 additions & 0 deletions examples/07-collaboration/10-versioning/.bnexample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"playground": true,
"docs": true,
"author": "matthewlipski",
"tags": ["Advanced", "Development", "Collaboration"],
"dependencies": {
"@y/protocols": "^1.0.6-rc.1",
"@y/websocket": "^4.0.0-3",
"@y/y": "^14.0.0-rc.16",
"react-icons": "5.6.0",
"@floating-ui/react": "^0.27.18",
"lib0": "1.0.0-rc.13"
}
}
Loading
Loading