Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading
Loading