Skip to main content

Setup

Step 1: Add Comment components

  • Add the Velt Comments component to the root of your app.
  • This component is required to render comments in your app.
  • Set the text mode prop to false to hide the default text comment tool.
  • CKEditor selections are handled by @veltdev/ckeditor-velt-comments.
  • Authenticate the user with authProvider and set the Velt document before users add comments.
  • Add VeltCommentsSidebar if you want a Google Docs-style comment sidebar.
import { VeltComments, VeltCommentsSidebar, VeltProvider, useSetDocument } from '@veltdev/react';

const user = {
  userId: 'user-1',
  organizationId: 'org-1',
  name: 'User One',
  email: 'user@example.com',
};

function VeltSetup() {
  useSetDocument('document-id', { documentName: 'Document name' });
  return null;
}

function App() {
  return (
    <VeltProvider apiKey="API_KEY" authProvider={{ user }}>
      <VeltSetup />
      <VeltComments textMode={false} />
      <VeltCommentsSidebar />
      {/* Your app content */}
    </VeltProvider>
  );
}

Step 2: Install the Velt CKEditor extension

npm i @veltdev/ckeditor-velt-comments @ckeditor/ckeditor5-react ckeditor5
ckeditor5 is a peer dependency of the Velt CKEditor package and must be provided by your app. Import the CKEditor CSS once in your app so the editor and the Velt overlay render correctly.
import 'ckeditor5/ckeditor5.css';

Step 3: Configure the CKEditor editor with the Velt Comments plugin

Add VeltCommentsPlugin to the CKEditor plugins list and configure it with veltComments. Capture the editor instance from onReady, then render Velt comment annotations into that instance with renderComments.
import { useEffect, useState } from 'react';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import {
  ClassicEditor,
  Essentials,
  Paragraph,
  Heading,
  Bold,
  Italic,
  Link,
  List,
  BlockQuote,
  type Editor,
} from 'ckeditor5';
import { useCommentAnnotations } from '@veltdev/react';
import {
  VeltCommentsPlugin,
  renderComments,
} from '@veltdev/ckeditor-velt-comments';
import 'ckeditor5/ckeditor5.css';

const EDITOR_ID = 'my-editor';

function CKEditorWithComments() {
  const [editor, setEditor] = useState<Editor | null>(null);
  const annotations = useCommentAnnotations();

  useEffect(() => {
    if (!editor) return;

    renderComments({
      editor,
      editorId: EDITOR_ID,
      commentAnnotations: annotations ?? [],
    });
  }, [editor, annotations]);

  return (
    <CKEditor
      editor={ClassicEditor}
      config={{
        licenseKey: 'GPL',
        plugins: [
          Essentials,
          Paragraph,
          Heading,
          Bold,
          Italic,
          Link,
          List,
          BlockQuote,
          VeltCommentsPlugin,
        ],
        toolbar: [
          'undo',
          'redo',
          '|',
          'heading',
          '|',
          'bold',
          'italic',
          'link',
          '|',
          'bulletedList',
          'numberedList',
          '|',
          'blockQuote',
        ],
        initialData: '<p>Select text, then add a comment.</p>',
        veltComments: { editorId: EDITOR_ID },
      }}
      onReady={(nextEditor) => setEditor(nextEditor)}
      onAfterDestroy={() => setEditor(null)}
    />
  );
}

Step 4: Add a comment button to your CKEditor editor

Add a button that users can click to add comments after selecting text in the CKEditor editor. Important: Use onMouseDown with preventDefault() so the browser does not move focus away from the editor before addComment reads the current selection. Keep the actual addComment call in onClick.
import { addComment } from '@veltdev/ckeditor-velt-comments';

const handleAddComment = () => {
  if (!editor) return;

  void addComment({
    editor,
    editorId: EDITOR_ID,
  });
};

<button
  onMouseDown={(event) => event.preventDefault()}
  onClick={handleAddComment}
>
  Add Comment
</button>

Step 5: Call addComment to add a comment

  • Call this method to add a comment to selected text in the CKEditor editor.
  • Params: AddCommentRequest. It has the following properties:
    • editor: CKEditor 5 Editor instance from onReady.
    • editorId: Id of the editor. Use this if you have multiple CKEditor editors on the same page. (optional)
    • context: CommentAnnotationContext for custom metadata to attach to the Comment Annotation. Learn more. (optional)
  • Returns: Promise<void>.
import { addComment } from '@veltdev/ckeditor-velt-comments';

const addCommentRequest = {
  editor,
  editorId: 'EDITOR_ID',
  context: {
    storyId: 'story-id',
    storyName: 'story-name',
  },
};

void addComment(addCommentRequest);
The library automatically writes context.textEditorConfig with the selected text, its occurrence index in the document, and the editor ID when one is provided.

Step 6: Render comments in CKEditor editor

  • Get the comment data from Velt SDK and render it in the CKEditor editor.
  • Params: RenderCommentsRequest. It has the following properties:
    • editor: CKEditor 5 Editor instance.
    • editorId: Id of the editor. Use this if you have multiple CKEditor editors on the same page. (optional)
    • commentAnnotations: Array of CommentAnnotation objects.
  • Returns: void.
import { useEffect } from 'react';
import { useCommentAnnotations } from '@veltdev/react';
import { renderComments } from '@veltdev/ckeditor-velt-comments';

const commentAnnotations = useCommentAnnotations();

useEffect(() => {
  if (!editor) return;

  renderComments({
    editor,
    editorId: 'EDITOR_ID',
    commentAnnotations: commentAnnotations ?? [],
  });
}, [editor, commentAnnotations]);

Step 7: Re-apply highlights after reloading content

Comment highlights are view-only overlay elements positioned over the commented text. They are not stored in the CKEditor document, so your saved HTML, schema, and undo history stay clean. Highlight positions are tracked with CKEditor model ranges and re-positioned as the document changes. Call renderComments whenever the editor is recreated or its content is replaced. The useEffect in Step 6 handles this automatically when the editor instance or the annotation list changes.

Step 8: Style the commented text

  • Comment highlights are rendered as velt-comment-text overlay markers.
  • You can override the marker styles in your app CSS.
velt-comment-text {
  background-color: rgba(255, 212, 0, 0.4);
  border-bottom: 2px solid #ffd400;
  cursor: pointer;
}

velt-comment-text:hover,
velt-comment-text[comment-selected="true"],
velt-comment-text.velt-comment-selected {
  background-color: rgba(255, 212, 0, 0.7);
}

Complete Example

APIs

VeltCommentsPlugin

The CKEditor 5 plugin that powers Velt comments. Add it to the <CKEditor> component’s config.plugins list and configure it with config.veltComments.
import { VeltCommentsPlugin } from '@veltdev/ckeditor-velt-comments';

<CKEditor
  editor={ClassicEditor}
  config={{
    plugins: [Essentials, Paragraph, VeltCommentsPlugin],
    veltComments: { editorId: 'my-editor' },
  }}
/>

addComment()

Creates a comment annotation for the currently selected text in CKEditor.
import { addComment } from '@veltdev/ckeditor-velt-comments';

void addComment({
  editor,
  editorId: 'my-editor',
});

renderComments()

Resolves and highlights Velt comment annotations in CKEditor.
import { renderComments } from '@veltdev/ckeditor-velt-comments';

renderComments({
  editor,
  editorId: 'my-editor',
  commentAnnotations: annotations ?? [],
});