|
| 1 | +--- |
| 2 | +title: Comments |
| 3 | +description: Learn how to enable comments in your BlockNote editor |
| 4 | +imageTitle: Comments |
| 5 | +--- |
| 6 | + |
| 7 | +import { Example } from "@/components/example"; |
| 8 | + |
| 9 | +# Comments |
| 10 | + |
| 11 | +BlockNote supports Comments, Comment Threads (replies) and emoji reactions out of the box. |
| 12 | + |
| 13 | +To enable comments in your editor, you need to: |
| 14 | + |
| 15 | +- provide a `resolveUsers` so BlockNote can retrieve and display user information (names and avatars). |
| 16 | +- provide a `ThreadStore` so BlockNote can store and retrieve comment threads. |
| 17 | +- enable real-time collaboration (see [Real-time collaboration](/docs/collaboration/real-time-collaboration)) |
| 18 | + |
| 19 | +```tsx |
| 20 | +const editor = useCreateBlockNote({ |
| 21 | + resolveUsers: async (userIds: string[]) => { |
| 22 | + // return user information for the given userIds (see below) |
| 23 | + }, |
| 24 | + comments: { |
| 25 | + threadStore: yourThreadStore, // see below |
| 26 | + }, |
| 27 | + // ... |
| 28 | + collaboration: { |
| 29 | + // ... // see real-time collaboration docs |
| 30 | + }, |
| 31 | +}); |
| 32 | +``` |
| 33 | + |
| 34 | +**Demo** |
| 35 | + |
| 36 | +<Example name="collaboration/comments" /> |
| 37 | + |
| 38 | +## ThreadStores |
| 39 | + |
| 40 | +A ThreadStore is used to store and retrieve comment threads. BlockNote is backend agnostic, so you can use any database or backend to store the threads. |
| 41 | +BlockNote comes with several built-in ThreadStore implementations: |
| 42 | + |
| 43 | +### `YjsThreadStore` |
| 44 | + |
| 45 | +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. |
| 46 | + |
| 47 | +```tsx |
| 48 | +import { YjsThreadStore } from "@blocknote/core"; |
| 49 | + |
| 50 | +const threadStore = new YjsThreadStore( |
| 51 | + userId, // The active user's ID |
| 52 | + yDoc.getMap("threads"), // Y.Map to store threads |
| 53 | + new DefaultThreadStoreAuth(userId, "editor"), // Authorization information, see below |
| 54 | +); |
| 55 | +``` |
| 56 | + |
| 57 | +_Note: While this is the easiest to implement, it requires users to have write access to the Yjs document to leave comments. Also, without proper server-side validation, any user could technically modify other users' comments._ |
| 58 | + |
| 59 | +### `RESTYjsThreadStore` |
| 60 | + |
| 61 | +The `RESTYjsThreadStore` combines Yjs storage with a REST API backend, providing secure comment management while maintaining real-time collaboration. This implementation is ideal when you have strong authentication requirements, but is a little more work to set up. |
| 62 | + |
| 63 | +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. |
| 64 | + |
| 65 | +```tsx |
| 66 | +import { RESTYjsThreadStore, DefaultThreadStoreAuth } from "@blocknote/core"; |
| 67 | + |
| 68 | +const threadStore = new RESTYjsThreadStore( |
| 69 | + "https://api.example.com/comments", // Base URL for the REST API |
| 70 | + { |
| 71 | + Authorization: "Bearer your-token", // Optional headers to add to requests |
| 72 | + }, |
| 73 | + yDoc.getMap("threads"), // Y.Map to retrieve commend data from |
| 74 | + new DefaultThreadStoreAuth(userId, "editor"), // Authorization rules (see below) |
| 75 | +); |
| 76 | +``` |
| 77 | + |
| 78 | +An example implementation of the REST API can be found in the [example repository](https://github.com/TypeCellOS/BlockNote-demo-nextjs-hocuspocus). |
| 79 | + |
| 80 | +_Note: Because writes are executed via a REST API, the `RESTYjsThreadStore` is not suitable for local-first applications that should be able to add and edit comments offline._ |
| 81 | + |
| 82 | +### `TiptapThreadStore` |
| 83 | + |
| 84 | +The `TiptapThreadStore` integrates with Tiptap's collaboration provider for comment management. This implementation is designed specifically for use with Tiptap's collaborative editing features. |
| 85 | + |
| 86 | +```tsx |
| 87 | +import { TiptapThreadStore, DefaultThreadStoreAuth } from "@blocknote/core"; |
| 88 | +import { TiptapCollabProvider } from "@hocuspocus/provider"; |
| 89 | + |
| 90 | +// Create a TiptapCollabProvider (you probably have this already) |
| 91 | +const provider = new TiptapCollabProvider({ |
| 92 | + name: "test", |
| 93 | + baseUrl: "https://collab.yourdomain.com", |
| 94 | + appId: "test", |
| 95 | + document: doc, |
| 96 | +}); |
| 97 | + |
| 98 | +// Create a TiptapThreadStore |
| 99 | +const threadStore = new TiptapThreadStore( |
| 100 | + userId, // The active user's ID |
| 101 | + provider, // Tiptap collaboration provider |
| 102 | + new DefaultThreadStoreAuth(userId, "editor"), // Authorization rules (see below) |
| 103 | +); |
| 104 | +``` |
| 105 | + |
| 106 | +### ThreadStoreAuth |
| 107 | + |
| 108 | +The `ThreadStoreAuth` class defines the authorization rules for interacting with comments. Every ThreadStore implementation requires a `ThreadStoreAuth` instance. BlockNote uses the `ThreadStoreAuth` instance to deterine which interactions are allowed for the current user (for example, whether they can create a new comment, edit or delete a comment, etc.). |
| 109 | + |
| 110 | +The `DefaultThreadStoreAuth` class provides a basic implementation of the `ThreadStoreAuth` class. It takes a user ID and a role ("comment" or "editor") and implements the rules. See the [source code](https://github.com/TypeCellOS/BlockNote/blob/main/packages/core/src/extensions/Comments/threadstore/DefaultThreadStoreAuth.ts) for more details. |
| 111 | + |
| 112 | +_Note: The `ThreadStoreAuth` only used to show / hide options in the UI. To secure comment related data, you still need to implement your own server-side validation (e.g. using `RESTYjsThreadStore` and a secure REST API)._ |
| 113 | + |
| 114 | +## `resolveUsers` function |
| 115 | + |
| 116 | +When a user interacts with a comment, the data is stored in the ThreadStore, along with the active user ID (as specified when initiating the ThreadStore). |
| 117 | + |
| 118 | +To display comments, BlockNote needs to retrieve user information (such as the username and avatar) based on the user ID. To do this, you need to provide a `resolveUsers` function in the editor options. |
| 119 | + |
| 120 | +This function is called with an array of user IDs, and should return an array of `User` objects in the same order. |
| 121 | + |
| 122 | +```tsx |
| 123 | +type User = { |
| 124 | + id: string; |
| 125 | + username: string; |
| 126 | + avatarUrl: string; |
| 127 | +}; |
| 128 | + |
| 129 | +async function myResolveUsers(userIds: string[]): Promise<User[]> { |
| 130 | + // fetch user information from your database / backend |
| 131 | + // and return an array of User objects |
| 132 | + |
| 133 | + return await callYourBackend(userIds); // |
| 134 | + |
| 135 | + // Return a list of users |
| 136 | + return users; |
| 137 | +} |
| 138 | +``` |
0 commit comments