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.
<VeltProvider apiKey="API_KEY">
  <VeltComments textMode={false} />
</VeltProvider>

Step 2: Install the Velt Lexical extension

npm install @veltdev/lexical-velt-comments @veltdev/client lexical

Step 3: Configure the Lexical editor

  • Register the CommentNode and configure your Lexical editor.
import { createEditor } from 'lexical';
import { CommentNode } from '@veltdev/lexical-velt-comments';

const editor = createEditor({
  namespace: 'MyEditor',
  nodes: [CommentNode],
  onError: (error) => console.error(error)
});

Step 4: Add a comment button to your Lexical editor

  • Add a button that appears in the context menu of your Lexical editor when the user selects text.
  • This button will be used to add a comment to the selected text.
import { addComment } from '@veltdev/lexical-velt-comments';

const CommentButton = ({ editor }) => {
  return (
    <Button
      onMouseDown={(e) => {
        e.preventDefault();
        addComment({ editor });
      }}
    >
      Comment
    </Button>
  );
};

Step 5: Call addComment to add a comment

  • Call this method to add a comment to selected text in the Lexical editor. You can use this when the user clicks on the comment button in context menu or presses a keyboard shortcut.
  • Params: AddCommentRequest
    • editorId: string, the id of the editor. Use this if you have multiple editors in your app.
    • editor: instance of the Lexical editor.
    • context: optional object to set the Comment Annotation’s custom metadata.
import { addComment } from '@veltdev/lexical-velt-comments';

const CommentButton = ({ editor }) => {
  return (
    <Button
      onMouseDown={(e) => {
        e.preventDefault();
        addComment({ editor });
      }}
    >
      Comment
    </Button>
  );
};

Step 6: Render Comments in Lexical editor

  • Get the comment data.
  • Render the comments in the Lexical editor.
  • Params: RenderCommentsRequest
    • editorId: string, the id of the editor. Use this if you have multiple editors in your app.
    • editor: instance of the Lexical editor.
    • commentAnnotations: CommentAnnotation[]
import { renderComments } from '@veltdev/lexical-velt-comments';

// Render existing comments from annotations
client.getCommentElement().getAllCommentAnnotations().subscribe((annotations) => {
  if (editor && annotations) {
    renderComments({ editor, commentAnnotations: annotations });
  }
});

Step 7: Remove comments from editor JSON

  • Use this utility to export editor JSON without Velt comment nodes.
import { exportJSONWithoutComments } from '@veltdev/lexical-velt-comments';

// Store clean state at your end
const cleanState = exportJSONWithoutComments(editor);

Step 8: Style the commented text

  • You can style the commented text by adding a CSS class to the velt-comment-text element.
  • By using the comment-available attribute, you can apply styles only when the comment data has loaded.
velt-comment-text[comment-available="true"] {
  background-color: #ffff00;
}

Complete Example

APIs

addComment()

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

<button
  onMouseDown={(e) => {
    e.preventDefault();
    addComment({ editor }); // optionally: { editor, editorId: 'doc-1', context: {...} }
  }}
>
  Comment
</button>

renderComments()

Renders and highlights comment annotations in the editor.
import { renderComments } from '@veltdev/lexical-velt-comments';
import type { CommentAnnotation } from '@veltdev/types';

// Example: stream annotations from your client and render into Lexical
client.getCommentElement().getAllCommentAnnotations().subscribe((annotations: CommentAnnotation[]) => {
  if (editor && annotations) {
    renderComments({ editor, commentAnnotations: annotations, editorId: 'doc-1' });
  }
});

CommentNode

A custom Lexical ElementNode that wraps text content with comment annotation metadata, including Velt annotation IDs.
  • Extends: ElementNode
  • Usage: Register the node with your Lexical editor so comment elements render correctly.
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { CommentNode } from '@veltdev/lexical-velt-comments';

const initialConfig = {
  namespace: 'MyEditor',
  nodes: [CommentNode], // enables Velt comment elements
  onError: console.error,
};

export default function Editor() {
  return <LexicalComposer initialConfig={initialConfig}>{/* plugins */}</LexicalComposer>;
}

exportJSONWithoutComments()

Exports the editor state as serialized JSON while stripping out all comment nodes and normalizing adjacent text nodes.
  • Signature: exportJSONWithoutComments(editor: LexicalEditor): SerializedEditorState<SerializedLexicalNode>
  • Params: editor: LexicalEditor
  • Returns: SerializedEditorState<SerializedLexicalNode>
import { exportJSONWithoutComments } from '@veltdev/lexical-velt-comments';

const cleanState = exportJSONWithoutComments(editor);