Mention extension for Tiptap rich text editor that enables @-mention functionality with customizable suggestion handling
npx @tessl/cli install tessl/npm-tiptap--extension-mention@3.4.0The @tiptap/extension-mention package provides a mention extension for the Tiptap rich text editor framework. It enables developers to implement @-mention functionality that integrates with Tiptap's suggestion system to provide real-time mention suggestions and rendering.
npm install @tiptap/extension-mention @tiptap/core @tiptap/pm @tiptap/suggestionimport { Mention } from "@tiptap/extension-mention";For default import:
import Mention from "@tiptap/extension-mention";For utility functions:
import { getSuggestionOptions } from "@tiptap/extension-mention";import { Editor } from "@tiptap/core";
import { Mention } from "@tiptap/extension-mention";
const editor = new Editor({
extensions: [
Mention.configure({
HTMLAttributes: {
class: "mention",
},
suggestion: {
items: ({ query }) => {
return [
{ id: 1, label: "Alice" },
{ id: 2, label: "Bob" },
{ id: 3, label: "Charlie" },
].filter(item =>
item.label.toLowerCase().startsWith(query.toLowerCase())
);
},
render: () => {
// Custom render implementation
return {
onStart: (props) => {},
onUpdate: (props) => {},
onKeyDown: (props) => {},
onExit: () => {},
};
},
},
}),
],
});The Mention extension is built around several key components:
Core mention extension that creates mention nodes in the editor.
/**
* Main mention extension class implementing ProseMirror node functionality
*/
export const Mention: Node<MentionOptions>;Utility functions for configuring mention suggestions.
/**
* Returns the suggestion options for a trigger of the Mention extension
* @param options Configuration options for the suggestion
* @returns Complete suggestion options for the trigger
*/
export function getSuggestionOptions(options: GetSuggestionOptionsOptions): SuggestionOptions;Configure the mention extension with options for rendering, suggestions, and behavior.
interface MentionOptions<SuggestionItem = any, Attrs extends Record<string, any> = MentionNodeAttrs> {
/** HTML attributes for mention nodes @default {} */
HTMLAttributes: Record<string, any>;
/** Function to render the text content of a mention */
renderText: (props: {
options: MentionOptions<SuggestionItem, Attrs>;
node: ProseMirrorNode;
suggestion: SuggestionOptions | null;
}) => string;
/** Function to render the HTML of a mention */
renderHTML: (props: {
options: MentionOptions<SuggestionItem, Attrs>;
node: ProseMirrorNode;
HTMLAttributes: Record<string, any>;
suggestion: SuggestionOptions | null;
}) => DOMOutputSpec;
/** Whether to delete the trigger character with backspace @default false */
deleteTriggerWithBackspace: boolean;
/** Multiple trigger configurations for different mention types @default [] */
suggestions: Array<Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>>;
/** Single trigger configuration (use suggestions for multiple triggers) @default {} */
suggestion: Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>;
/** @deprecated Use renderText and renderHTML instead */
renderLabel?: (props: {
options: MentionOptions<SuggestionItem, Attrs>;
node: ProseMirrorNode;
suggestion: SuggestionOptions | null;
}) => string;
}Attributes stored on mention nodes for data persistence and rendering.
interface MentionNodeAttrs {
/** Identifier for the mentioned item, stored as data-id attribute */
id: string | null;
/** Display label for the mention, stored as data-label attribute */
label?: string | null;
/** Trigger character that activated this mention, stored as data-mention-suggestion-char @default '@' */
mentionSuggestionChar?: string;
}
interface GetSuggestionOptionsOptions {
/** The Tiptap editor instance */
editor: Editor;
/** The suggestion options configuration provided to the Mention extension */
overrideSuggestionOptions: Omit<SuggestionOptions, 'editor'>;
/** The name of the Mention extension */
extensionName: string;
/** The character that triggers the suggestion */
char?: string;
}The extension relies on types from its peer dependencies:
Editor from @tiptap/core - The main Tiptap editor instanceDOMOutputSpec from @tiptap/pm/model - ProseMirror DOM output specificationProseMirrorNode from @tiptap/pm/model - ProseMirror node with attributesSuggestionOptions from @tiptap/suggestion - Configuration for suggestion functionalityUsage Examples:
import { Editor } from "@tiptap/core";
import { Mention } from "@tiptap/extension-mention";
// Basic mention setup
const editor = new Editor({
extensions: [
Mention.configure({
HTMLAttributes: {
class: "mention",
},
suggestion: {
items: ({ query }) => {
return users.filter(user =>
user.name.toLowerCase().includes(query.toLowerCase())
);
},
render: () => ({
onStart: (props) => {
// Show suggestion popup
},
onUpdate: (props) => {
// Update suggestions
},
onKeyDown: (props) => {
// Handle keyboard navigation
if (props.event.key === "Enter") {
return true; // Handled
}
return false;
},
onExit: () => {
// Hide popup
},
}),
},
}),
],
});
// Multiple trigger characters
const editorWithMultipleTriggers = new Editor({
extensions: [
Mention.configure({
suggestions: [
{
char: "@",
items: ({ query }) => getUserSuggestions(query),
render: () => createUserMentionRenderer(),
},
{
char: "#",
items: ({ query }) => getTagSuggestions(query),
render: () => createTagMentionRenderer(),
},
],
}),
],
});
// Custom rendering
const editorWithCustomRender = new Editor({
extensions: [
Mention.configure({
renderText: ({ node, suggestion }) => {
return `${suggestion?.char ?? "@"}${node.attrs.label ?? node.attrs.id}`;
},
renderHTML: ({ node, HTMLAttributes, suggestion }) => {
return [
"span",
{
...HTMLAttributes,
"data-type": "mention",
"data-id": node.attrs.id,
class: "custom-mention",
},
`${suggestion?.char ?? "@"}${node.attrs.label ?? node.attrs.id}`,
];
},
suggestion: {
items: ({ query }) => getMentionItems(query),
render: () => createMentionRenderer(),
},
}),
],
});