Typix LogoTypix
Extensions

Mention

@mention typeahead with async search and custom rendering

The Mention extension provides a typeahead mention system (like @mentions in Slack or GitHub) with async search, debouncing, and fully customizable UI.

Demo

Loading preview...
import {  EditorContent,  EditorRoot,  createEditorConfig,  defaultExtensionNodes,} from "@typix-editor/react";import {  MentionExtension,  MentionNode,} from "@typix-editor/extension-mention";import { theme } from "./theme";import "./style.css";const config = createEditorConfig({  extensionNodes: [...defaultExtensionNodes, MentionNode],  theme,});const users = [  { id: "1", name: "Alice Johnson" },  { id: "2", name: "Bob Smith" },  { id: "3", name: "Charlie Brown" },];export default function MentionExample() {  return (    <EditorRoot config={config}>      <EditorContent placeholder='Type "@" to mention someone...' />      <MentionExtension        onSearch={(query) =>          users.filter((u) =>            u.name.toLowerCase().includes(query.toLowerCase()),          )        }        maxSuggestions={5}      />    </EditorRoot>  );}

Installation

pnpm add @typix-editor/extension-mention
npm install @typix-editor/extension-mention
yarn add @typix-editor/extension-mention

Setup

Register the MentionNode and add the extension:

import { createEditorConfig, defaultExtensionNodes } from '@typix-editor/react';
import { MentionExtension, MentionNode } from '@typix-editor/extension-mention';

const config = createEditorConfig({
  extensionNodes: [...defaultExtensionNodes, MentionNode],
});

function MyEditor() {
  return (
    <EditorRoot config={config}>
      <EditorContent />
      <MentionExtension
        onSearch={async (query) => {
          const res = await fetch(`/api/users?q=${query}`);
          return res.json();
        }}
      />
    </EditorRoot>
  );
}

With custom item rendering

<MentionExtension
  onSearch={searchUsers}
  onSelect={(item) => console.log('Selected:', item)}
  renderMenuItem={(props) => (
    <div
      className={props.isSelected ? 'bg-blue-50' : ''}
      onClick={props.onClick}
      onMouseEnter={props.onMouseEnter}
    >
      <img src={props.item.data?.avatar} alt="" />
      <span>{props.item.name}</span>
    </div>
  )}
  triggerConfig={{ trigger: '@', minLength: 1 }}
  maxSuggestions={5}
  debounceMs={300}
/>

Props

PropTypeDefaultDescription
onSearch(query: string, trigger: string) => Promise<MentionItem[]> | MentionItem[]Search function (required)
onSelect(item: MentionItem) => voidCalled when a mention is selected
onMenuOpen() => voidCalled when the menu opens
onMenuClose() => voidCalled when the menu closes
triggerConfigMentionTriggerConfig{ trigger: '@' }Trigger behavior config
nodeConfigMentionNodeConfig{}Node appearance config
maxSuggestionsnumber10Max items to display
debounceMsnumber200Search debounce delay (ms)
renderMenu(props: MentionMenuProps) => JSX.ElementCustom menu renderer
renderMenuItem(props: MentionMenuItemProps) => ReactNodeCustom item renderer
loadingContentReactNodeContent shown while loading
emptyContentReactNodeContent shown for no results
menuClassNamestringCSS class for the menu
disabledbooleanfalseDisable the extension

MentionTriggerConfig

OptionTypeDefaultDescription
triggerstring"@"Trigger character
minLengthnumber0Min query length before searching
maxLengthnumber75Max query length
allowSpacesbooleantrueAllow spaces in queries

MentionItem

interface MentionItem {
  id: string;
  name: string;
  data?: Record<string, unknown>;
}

Exports

ExportTypeDescription
MentionExtensionComponentThe mention plugin
MentionNodeLexical NodeCustom mention node
$createMentionNodeFunctionCreate a mention node
$isMentionNodeFunctionType guard for mention nodes
configureMentionNodeFunctionConfigure global mention node options

Nodes

NodeDescription
MentionNodeInline node representing a mention. Stores id, name, trigger, and optional data.

On this page