Plate
PlateEditorsTemplates
GitHub16kGitHub
DiscordDiscord
    • Stream
    • Copilot
  • Comments
  • Discussion
  • Suggestions
    • Basic Blocks
      • Blockquote
      • Heading
      • Horizontal Rule
    • Callout
    • Code Block
    • Column
    • Date
    • Equation
    • Link
    • Media
    • MentionElement
    • Table
    • Table of Contents
    • Footnote
    • Details
  • Marks
    • Bold
    • Italic
    • Underline
    • Code
    • Highlight
    • Keyboard Input
    • Strikethrough
    • Subscript
    • Superscript
      • Font
      • Line Height
      • Text Align
    • Indent
    • List
      • Exit Break
      • Single Block
      • Trailing Block
    • Autoformat
    • Block Menu
    • Block Placeholder
    • Combobox
      • Emoji
      • MentionElement
      • Slash Command
    • Drag & Drop
    • Navigation Feedback
    • Tabbable
    • Toolbar
    • Yjs
    • Multi SelectEditor
    • CSV
    • DOCX
    • HTML
    • Markdown

Mention

PreviousNext

Inline markable void mentions completed from text typed after a trigger.

ComboboxMention NodesInline Combobox

Typing @ opens a popup that filters on the text typed after it. Selecting an item replaces that text with a markable void mention node. The package owns the trigger policy, mention insertion, selection movement and Markdown mention serialization. The registry owns the demo item list and the popup UI.

Loading…
MediaTable

On This Page

FeaturesFast pathAdd the kitRender mentionsAdd static renderingOwnershipManual setupInstall packageAdd pluginsSelect an itemValue shapeTriggerMarkdownAPI Reference
Build your editor
Production-ready AI template and reusable components.
Get all-access

Features

  • Inline void mention nodes with required ref and optional label.
  • A popup over the text typed after @, through Combobox.
  • Configurable trigger strings, regexes, and trigger queries.
  • Markable void rendering so bold, italic, and underline can style a mention.
  • Optional trailing space after selected mention items.
  • Markdown format through [display text](mention:ref) links.
Report an issue

Fast path

Add the kit

MentionKit installs MentionPlugin with the registry mention node, the mention popup, and a trigger rule that allows @ at the start of a line, after whitespace, or after quotes.

'use client';
 
import { IS_APPLE } from 'platejs';
import { MentionPlugin } from 'platejs/mention/react';
import {
  type EditorElementProps,
  EditorElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
import { useMounted } from '@/hooks/use-mounted';
import { inlineSuggestionVariants } from '@/lib/inline-suggestion';
 
import {
  InlineCombobox,
  InlineComboboxContent,
  InlineComboboxEmpty,
  InlineComboboxGroup,
  InlineComboboxItem,
} from './inline-combobox';
 
export function MentionElement(
  props: EditorElementProps<typeof MentionPlugin> & {
    prefix?: string;
  }
) {
  const { element } = props;
  const selected = useElementSelected();
  const focused = useEditorFocused();
  const mounted = useMounted();
  const readOnly = useEditorReadOnly();
  const label = element.label ?? element.ref;
 
  return (
    <EditorElement
      {...props}
      className={cn(
        'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
        inlineSuggestionVariants(),
        !readOnly && 'cursor-pointer',
        selected && focused && 'ring-2 ring-ring',
        element.children[0].bold === true && 'font-bold',
        element.children[0].italic === true && 'italic',
        element.children[0].underline === true && 'underline'
      )}
      attributes={{
        ...props.attributes,
        contentEditable: false,
        'data-editor-value': label,
        draggable: true,
      }}
    >
      {mounted && IS_APPLE ? (
        // Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490
        <>
          {props.children}
          {props.prefix}
          {label}
        </>
      ) : (
        // Others like Android https://github.com/ianstormtaylor/slate/pull/5360
        <>
          {props.prefix}
          {label}
          {props.children}
        </>
      )}
    </EditorElement>
  );
}
 
export function MentionCombobox({
  editableRef,
}: {
  editableRef: React.RefObject<HTMLDivElement | null>;
}) {
  return (
    <InlineCombobox editableRef={editableRef} plugin={MentionPlugin}>
      <InlineComboboxContent className="my-1.5">
        <InlineComboboxEmpty>No results</InlineComboboxEmpty>
 
        <InlineComboboxGroup>
          {MENTIONABLES.map((item) => (
            <InlineComboboxItem
              key={item.ref}
              value={item.label}
              onSelect={(tx) => {
                tx.plugin(MentionPlugin).insert({
                  label: item.label,
                  ref: item.ref,
                });
              }}
            >
              {item.label}
            </InlineComboboxItem>
          ))}
        </InlineComboboxGroup>
      </InlineComboboxContent>
    </InlineCombobox>
  );
}
 
const MENTIONABLES = [
  { ref: '0', label: 'Aayla Secura' },
  { ref: '1', label: 'Adi Gallia' },
  {
    ref: '2',
    label: 'Admiral Dodd Rancit',
  },
  {
    ref: '3',
    label: 'Admiral Firmus Piett',
  },
  {
    ref: '4',
    label: 'Admiral Gial Ackbar',
  },
  { ref: '5', label: 'Admiral Ozzel' },
  { ref: '6', label: 'Admiral Raddus' },
  {
    ref: '7',
    label: 'Admiral Terrinald Screed',
  },
  { ref: '8', label: 'Admiral Trench' },
  {
    ref: '9',
    label: 'Admiral U.O. Statura',
  },
  { ref: '10', label: 'Agen Kolar' },
  { ref: '11', label: 'Agent Kallus' },
  {
    ref: '12',
    label: 'Aiolin and Morit Astarte',
  },
  { ref: '13', label: 'Aks Moe' },
  { ref: '14', label: 'Almec' },
  { ref: '15', label: 'Alton Kastle' },
  { ref: '16', label: 'Amee' },
  { ref: '17', label: 'AP-5' },
  { ref: '18', label: 'Armitage Hux' },
  { ref: '19', label: 'Artoo' },
  { ref: '20', label: 'Arvel Crynyd' },
  { ref: '21', label: 'Asajj Ventress' },
  { ref: '22', label: 'Aurra Sing' },
  { ref: '23', label: 'AZI-3' },
  { ref: '24', label: 'Bala-Tik' },
  { ref: '25', label: 'Barada' },
  { ref: '26', label: 'Bargwill Tomder' },
  { ref: '27', label: 'Baron Papanoida' },
  { ref: '28', label: 'Barriss Offee' },
  { ref: '29', label: 'Baze Malbus' },
  { ref: '30', label: 'Bazine Netal' },
  { ref: '31', label: 'BB-8' },
  { ref: '32', label: 'BB-9E' },
  { ref: '33', label: 'Ben Quadinaros' },
  { ref: '34', label: 'Berch Teller' },
  { ref: '35', label: 'Beru Lars' },
  { ref: '36', label: 'Bib Fortuna' },
  {
    ref: '37',
    label: 'Biggs Darklighter',
  },
  { ref: '38', label: 'Black Krrsantan' },
  { ref: '39', label: 'Bo-Katan Kryze' },
  { ref: '40', label: 'Boba Fett' },
  { ref: '41', label: 'Bobbajo' },
  { ref: '42', label: 'Bodhi Rook' },
  { ref: '43', label: 'Borvo the Hutt' },
  { ref: '44', label: 'Boss Nass' },
  { ref: '45', label: 'Bossk' },
  {
    ref: '46',
    label: 'Breha Antilles-Organa',
  },
  { ref: '47', label: 'Bren Derlin' },
  { ref: '48', label: 'Brendol Hux' },
  { ref: '49', label: 'BT-1' },
];
 
export const MentionKit = [
  MentionPlugin.configure({
    component: MentionElement,
    initialState: {
      triggerPreviousCharPattern: /^$|^[\s"']$/,
    },
    slots: { afterEditable: MentionCombobox },
  }),
];
'use client';
 
import { IS_APPLE } from 'platejs';
import { MentionPlugin } from 'platejs/mention/react';
import {
  type EditorElementProps,
  EditorElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
import { useMounted } from '@/hooks/use-mounted';
import { inlineSuggestionVariants } from '@/lib/inline-suggestion';
 
import {













































































































































































import { createEditor } from 'platejs/react';
 
import { MentionKit } from '@/components/editor/mention';
 
export const editor = createEditor({
  plugins: MentionKit,
});
import { createEditor } from 'platejs/react';
 
import { MentionKit } from '@/components/editor/mention';
 
export const editor = createEditor({
  plugins: MentionKit,

Render mentions

mention renders the mention node and the MentionCombobox popup. The demo data lives in that registry UI file, so replace it with your app users, pages, or records.

'use client';
 
import { IS_APPLE } from 'platejs';
import { MentionPlugin } from 'platejs/mention/react';
import {
  type EditorElementProps,
  EditorElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
import { useMounted } from '@/hooks/use-mounted';
import { inlineSuggestionVariants } from '@/lib/inline-suggestion';
 
import













































































































































































Add static rendering

mention-static uses BaseMentionPlugin with the static mention node for read-only output.

import { BaseMentionPlugin } from 'platejs/mention';
import { type EditorElementProps, EditorElement } from 'platejs/static';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
 
export function MentionElementStatic(
  props: EditorElementProps<typeof BaseMentionPlugin> & {
    prefix?: string;
  }
) {
  const { prefix } = props;
  const { element } = props;




























Ownership

LayerOwnerWhat It Does
platejsPackageExports BaseMentionPlugin and the scoped mention update.
platejs/reactPackageExports MentionPlugin.
platejs/combobox/reactPackageExports useCombobox, which opens the popup on a typed trigger.
mentionRegistryAdds MentionPlugin with the mention node and popup.
mention-staticRegistryAdds BaseMentionPlugin.configure({ component: MentionElementStatic }).
mentionRegistry UIRenders mention atoms, the popup, and demo items.
inline-comboboxRegistry UIRenders the Ariakit listbox, groups, items, and empty state at the trigger.
platejs/markdownPackageSerializes mentions as mention: links and parses them back.

Manual setup

Install package

pnpm add platejs @tanstack/react-virtual
pnpm add platejs @tanstack/react-virtual

Add plugins

Mount the popup in the plugin's afterEditable slot:

import { MentionPlugin } from 'platejs/mention/react';
import { createEditor } from 'platejs/react';
 
import {
  MentionCombobox,
  MentionElement,
} from '@/components/editor/mention';
 
export const editor = createEditor({
  plugins: [
    MentionPlugin.configure({
      component: MentionElement,
      initialState: {
        triggerPreviousCharPattern: /^$|^[\s"']$/,
      },
      slots: { afterEditable: MentionCombobox },
    }),
  ],
});

Select an item

Call the mention update from the item's onSelect. The popup first removes the typed @ and query in the same transaction. The update inserts the mention, moves the cursor after it, and inserts a trailing space only when insertSpaceAfterMention is enabled and the mention lands at the end of the block.

<InlineComboboxItem
  value={item.label}
  onSelect={(tx) => {
    tx.plugin(MentionPlugin).insert({
      label: item.label,
      ref: item.ref,
    });
  }}
>
  {item.label}
</InlineComboboxItem>
<InlineComboboxItem
  value={item.label}
  onSelect={(tx) 







Value shape

PLUGINS.mention identifies the capability and its default persisted element type is mention. Reusable code reads the configured type through the plugin portal's schema.type.

const value = [
  {
    children: [
      { text: 'Assigned to ' },
      {
        children: [{ text: '' }],
        label: 'Jane Smith',
        ref: 'user_123',
        type: 'mention',
      },
      { text: '.' },
    ],
    type: 'paragraph',
  },
];
const value = [
  {
    children: [
      { text: 'Assigned to ' },
      {
        children: [{ text: '' }],
        label: 'Jane Smith',
        ref: 'user_123',
        type: 'mention',
      },
      { text: '.' },
    ],
    type: 'paragraph',
  },
];
FieldTypeNotes
type'mention'Inline void mention node.
refstringRequired persisted association with the mentioned entity.
labelstringOptional visible text; rendering falls back to ref.
children[{ text: '' }]Empty child required for Plate inline void nodes.

The mention element declares schema.element.void: 'markable-inline', so marks on its empty child can style the rendered mention.

Trigger

The popup opens when the user types @ and the combobox rules allow it.

GateSource
Typed text matches triggerstring, string[], or RegExp.
Previous character matches triggerPreviousCharPatternDefaults to /^\s?$/; registry kit uses /^$|^[\s"']$/.
triggerQuery(editor) returns trueOptional app veto for custom contexts.

Until an item is selected, @ and the query are ordinary text in the document. Escape closes the popup and keeps that text.

Markdown

platejs/markdown writes a mention as a link. The URL is mention: followed by the URL-encoded ref, and the link text is label ?? ref.

Hello [Jane Smith](mention:user_123).
Hello [Jane Smith](mention:user_123).

Parsing reads mention: links back into mention nodes. Other links such as [@docs](/docs/mention) stay links, and bare @alice text stays text.

API Reference

APIPackageUse
BaseMentionPluginplatejsHeadless inline markable void mention plugin with a ComboboxState trigger policy.
MentionPluginplatejs/reactReact mention plugin.
editor.plugin(MentionPlugin).update.insert({ ref, label? })BaseMentionPlugin updateInserts the mention node at the current selection.
useComboboxplatejs/combobox/reactOpens the popup on a typed @ and completes the query.
inline-comboboxCopied registry UIHandles the listbox, keyboard navigation and filtering for the mention popup.
InlineCombobox,
InlineComboboxContent,
InlineComboboxEmpty,
InlineComboboxGroup,
InlineComboboxItem,
} from './inline-combobox';
export function MentionElement(
props: EditorElementProps<typeof MentionPlugin> & {
prefix?: string;
}
) {
const { element } = props;
const selected = useElementSelected();
const focused = useEditorFocused();
const mounted = useMounted();
const readOnly = useEditorReadOnly();
const label = element.label ?? element.ref;
return (
<EditorElement
{...props}
className={cn(
'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
inlineSuggestionVariants(),
!readOnly && 'cursor-pointer',
selected && focused && 'ring-2 ring-ring',
element.children[0].bold === true && 'font-bold',
element.children[0].italic === true && 'italic',
element.children[0].underline === true && 'underline'
)}
attributes={{
...props.attributes,
contentEditable: false,
'data-editor-value': label,
draggable: true,
}}
>
{mounted && IS_APPLE ? (
// Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490
<>
{props.children}
{props.prefix}
{label}
</>
) : (
// Others like Android https://github.com/ianstormtaylor/slate/pull/5360
<>
{props.prefix}
{label}
{props.children}
</>
)}
</EditorElement>
);
}
export function MentionCombobox({
editableRef,
}: {
editableRef: React.RefObject<HTMLDivElement | null>;
}) {
return (
<InlineCombobox editableRef={editableRef} plugin={MentionPlugin}>
<InlineComboboxContent className="my-1.5">
<InlineComboboxEmpty>No results</InlineComboboxEmpty>
<InlineComboboxGroup>
{MENTIONABLES.map((item) => (
<InlineComboboxItem
key={item.ref}
value={item.label}
onSelect={(tx) => {
tx.plugin(MentionPlugin).insert({
label: item.label,
ref: item.ref,
});
}}
>
{item.label}
</InlineComboboxItem>
))}
</InlineComboboxGroup>
</InlineComboboxContent>
</InlineCombobox>
);
}
const MENTIONABLES = [
{ ref: '0', label: 'Aayla Secura' },
{ ref: '1', label: 'Adi Gallia' },
{
ref: '2',
label: 'Admiral Dodd Rancit',
},
{
ref: '3',
label: 'Admiral Firmus Piett',
},
{
ref: '4',
label: 'Admiral Gial Ackbar',
},
{ ref: '5', label: 'Admiral Ozzel' },
{ ref: '6', label: 'Admiral Raddus' },
{
ref: '7',
label: 'Admiral Terrinald Screed',
},
{ ref: '8', label: 'Admiral Trench' },
{
ref: '9',
label: 'Admiral U.O. Statura',
},
{ ref: '10', label: 'Agen Kolar' },
{ ref: '11', label: 'Agent Kallus' },
{
ref: '12',
label: 'Aiolin and Morit Astarte',
},
{ ref: '13', label: 'Aks Moe' },
{ ref: '14', label: 'Almec' },
{ ref: '15', label: 'Alton Kastle' },
{ ref: '16', label: 'Amee' },
{ ref: '17', label: 'AP-5' },
{ ref: '18', label: 'Armitage Hux' },
{ ref: '19', label: 'Artoo' },
{ ref: '20', label: 'Arvel Crynyd' },
{ ref: '21', label: 'Asajj Ventress' },
{ ref: '22', label: 'Aurra Sing' },
{ ref: '23', label: 'AZI-3' },
{ ref: '24', label: 'Bala-Tik' },
{ ref: '25', label: 'Barada' },
{ ref: '26', label: 'Bargwill Tomder' },
{ ref: '27', label: 'Baron Papanoida' },
{ ref: '28', label: 'Barriss Offee' },
{ ref: '29', label: 'Baze Malbus' },
{ ref: '30', label: 'Bazine Netal' },
{ ref: '31', label: 'BB-8' },
{ ref: '32', label: 'BB-9E' },
{ ref: '33', label: 'Ben Quadinaros' },
{ ref: '34', label: 'Berch Teller' },
{ ref: '35', label: 'Beru Lars' },
{ ref: '36', label: 'Bib Fortuna' },
{
ref: '37',
label: 'Biggs Darklighter',
},
{ ref: '38', label: 'Black Krrsantan' },
{ ref: '39', label: 'Bo-Katan Kryze' },
{ ref: '40', label: 'Boba Fett' },
{ ref: '41', label: 'Bobbajo' },
{ ref: '42', label: 'Bodhi Rook' },
{ ref: '43', label: 'Borvo the Hutt' },
{ ref: '44', label: 'Boss Nass' },
{ ref: '45', label: 'Bossk' },
{
ref: '46',
label: 'Breha Antilles-Organa',
},
{ ref: '47', label: 'Bren Derlin' },
{ ref: '48', label: 'Brendol Hux' },
{ ref: '49', label: 'BT-1' },
];
export const MentionKit = [
MentionPlugin.configure({
component: MentionElement,
initialState: {
triggerPreviousCharPattern: /^$|^[\s"']$/,
},
slots: { afterEditable: MentionCombobox },
}),
];
});
{
InlineCombobox,
InlineComboboxContent,
InlineComboboxEmpty,
InlineComboboxGroup,
InlineComboboxItem,
} from './inline-combobox';
export function MentionElement(
props: EditorElementProps<typeof MentionPlugin> & {
prefix?: string;
}
) {
const { element } = props;
const selected = useElementSelected();
const focused = useEditorFocused();
const mounted = useMounted();
const readOnly = useEditorReadOnly();
const label = element.label ?? element.ref;
return (
<EditorElement
{...props}
className={cn(
'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
inlineSuggestionVariants(),
!readOnly && 'cursor-pointer',
selected && focused && 'ring-2 ring-ring',
element.children[0].bold === true && 'font-bold',
element.children[0].italic === true && 'italic',
element.children[0].underline === true && 'underline'
)}
attributes={{
...props.attributes,
contentEditable: false,
'data-editor-value': label,
draggable: true,
}}
>
{mounted && IS_APPLE ? (
// Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490
<>
{props.children}
{props.prefix}
{label}
</>
) : (
// Others like Android https://github.com/ianstormtaylor/slate/pull/5360
<>
{props.prefix}
{label}
{props.children}
</>
)}
</EditorElement>
);
}
export function MentionCombobox({
editableRef,
}: {
editableRef: React.RefObject<HTMLDivElement | null>;
}) {
return (
<InlineCombobox editableRef={editableRef} plugin={MentionPlugin}>
<InlineComboboxContent className="my-1.5">
<InlineComboboxEmpty>No results</InlineComboboxEmpty>
<InlineComboboxGroup>
{MENTIONABLES.map((item) => (
<InlineComboboxItem
key={item.ref}
value={item.label}
onSelect={(tx) => {
tx.plugin(MentionPlugin).insert({
label: item.label,
ref: item.ref,
});
}}
>
{item.label}
</InlineComboboxItem>
))}
</InlineComboboxGroup>
</InlineComboboxContent>
</InlineCombobox>
);
}
const MENTIONABLES = [
{ ref: '0', label: 'Aayla Secura' },
{ ref: '1', label: 'Adi Gallia' },
{
ref: '2',
label: 'Admiral Dodd Rancit',
},
{
ref: '3',
label: 'Admiral Firmus Piett',
},
{
ref: '4',
label: 'Admiral Gial Ackbar',
},
{ ref: '5', label: 'Admiral Ozzel' },
{ ref: '6', label: 'Admiral Raddus' },
{
ref: '7',
label: 'Admiral Terrinald Screed',
},
{ ref: '8', label: 'Admiral Trench' },
{
ref: '9',
label: 'Admiral U.O. Statura',
},
{ ref: '10', label: 'Agen Kolar' },
{ ref: '11', label: 'Agent Kallus' },
{
ref: '12',
label: 'Aiolin and Morit Astarte',
},
{ ref: '13', label: 'Aks Moe' },
{ ref: '14', label: 'Almec' },
{ ref: '15', label: 'Alton Kastle' },
{ ref: '16', label: 'Amee' },
{ ref: '17', label: 'AP-5' },
{ ref: '18', label: 'Armitage Hux' },
{ ref: '19', label: 'Artoo' },
{ ref: '20', label: 'Arvel Crynyd' },
{ ref: '21', label: 'Asajj Ventress' },
{ ref: '22', label: 'Aurra Sing' },
{ ref: '23', label: 'AZI-3' },
{ ref: '24', label: 'Bala-Tik' },
{ ref: '25', label: 'Barada' },
{ ref: '26', label: 'Bargwill Tomder' },
{ ref: '27', label: 'Baron Papanoida' },
{ ref: '28', label: 'Barriss Offee' },
{ ref: '29', label: 'Baze Malbus' },
{ ref: '30', label: 'Bazine Netal' },
{ ref: '31', label: 'BB-8' },
{ ref: '32', label: 'BB-9E' },
{ ref: '33', label: 'Ben Quadinaros' },
{ ref: '34', label: 'Berch Teller' },
{ ref: '35', label: 'Beru Lars' },
{ ref: '36', label: 'Bib Fortuna' },
{
ref: '37',
label: 'Biggs Darklighter',
},
{ ref: '38', label: 'Black Krrsantan' },
{ ref: '39', label: 'Bo-Katan Kryze' },
{ ref: '40', label: 'Boba Fett' },
{ ref: '41', label: 'Bobbajo' },
{ ref: '42', label: 'Bodhi Rook' },
{ ref: '43', label: 'Borvo the Hutt' },
{ ref: '44', label: 'Boss Nass' },
{ ref: '45', label: 'Bossk' },
{
ref: '46',
label: 'Breha Antilles-Organa',
},
{ ref: '47', label: 'Bren Derlin' },
{ ref: '48', label: 'Brendol Hux' },
{ ref: '49', label: 'BT-1' },
];
export const MentionKit = [
MentionPlugin.configure({
component: MentionElement,
initialState: {
triggerPreviousCharPattern: /^$|^[\s"']$/,
},
slots: { afterEditable: MentionCombobox },
}),
];
'use client';
 
import { IS_APPLE } from 'platejs';
import { MentionPlugin } from 'platejs/mention/react';
import {
  type EditorElementProps,
  EditorElement,
  useEditorFocused,
  useEditorReadOnly,
  useElementSelected,
} from 'platejs/react';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
import { useMounted } from '@/hooks/use-mounted';
import { inlineSuggestionVariants } from '@/lib/inline-suggestion';
 
import {
  InlineCombobox,
  InlineComboboxContent,
  InlineComboboxEmpty,
  InlineComboboxGroup,
  InlineComboboxItem,
} from './inline-combobox';
 
export function MentionElement(
  props: EditorElementProps<typeof MentionPlugin> & {
    prefix?: string;
  }
) {
  const { element } = props;
  const selected = useElementSelected();
  const focused = useEditorFocused();
  const mounted = useMounted();
  const readOnly = useEditorReadOnly();
  const label = element.label ?? element.ref;
 
  return (
    <EditorElement
      {...props}
      className={cn(
        'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
        inlineSuggestionVariants(),
        !readOnly && 'cursor-pointer',
        selected && focused && 'ring-2 ring-ring',
        element.children[0].bold === true && 'font-bold',
        element.children[0].italic === true && 'italic',
        element.children[0].underline === true && 'underline'
      )}
      attributes={{
        ...props.attributes,
        contentEditable: false,
        'data-editor-value': label,
        draggable: true,
      }}
    >
      {mounted && IS_APPLE ? (
        // Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490
        <>
          {props.children}
          {props.prefix}
          {label}
        </>
      ) : (
        // Others like Android https://github.com/ianstormtaylor/slate/pull/5360
        <>
          {props.prefix}
          {label}
          {props.children}
        </>
      )}
    </EditorElement>
  );
}
 
export function MentionCombobox({
  editableRef,
}: {
  editableRef: React.RefObject<HTMLDivElement | null>;
}) {
  return (
    <InlineCombobox editableRef={editableRef} plugin={MentionPlugin}>
      <InlineComboboxContent className="my-1.5">
        <InlineComboboxEmpty>No results</InlineComboboxEmpty>
 
        <InlineComboboxGroup>
          {MENTIONABLES.map((item) => (
            <InlineComboboxItem
              key={item.ref}
              value={item.label}
              onSelect={(tx) => {
                tx.plugin(MentionPlugin).insert({
                  label: item.label,
                  ref: item.ref,
                });
              }}
            >
              {item.label}
            </InlineComboboxItem>
          ))}
        </InlineComboboxGroup>
      </InlineComboboxContent>
    </InlineCombobox>
  );
}
 
const MENTIONABLES = [
  { ref: '0', label: 'Aayla Secura' },
  { ref: '1', label: 'Adi Gallia' },
  {
    ref: '2',
    label: 'Admiral Dodd Rancit',
  },
  {
    ref: '3',
    label: 'Admiral Firmus Piett',
  },
  {
    ref: '4',
    label: 'Admiral Gial Ackbar',
  },
  { ref: '5', label: 'Admiral Ozzel' },
  { ref: '6', label: 'Admiral Raddus' },
  {
    ref: '7',
    label: 'Admiral Terrinald Screed',
  },
  { ref: '8', label: 'Admiral Trench' },
  {
    ref: '9',
    label: 'Admiral U.O. Statura',
  },
  { ref: '10', label: 'Agen Kolar' },
  { ref: '11', label: 'Agent Kallus' },
  {
    ref: '12',
    label: 'Aiolin and Morit Astarte',
  },
  { ref: '13', label: 'Aks Moe' },
  { ref: '14', label: 'Almec' },
  { ref: '15', label: 'Alton Kastle' },
  { ref: '16', label: 'Amee' },
  { ref: '17', label: 'AP-5' },
  { ref: '18', label: 'Armitage Hux' },
  { ref: '19', label: 'Artoo' },
  { ref: '20', label: 'Arvel Crynyd' },
  { ref: '21', label: 'Asajj Ventress' },
  { ref: '22', label: 'Aurra Sing' },
  { ref: '23', label: 'AZI-3' },
  { ref: '24', label: 'Bala-Tik' },
  { ref: '25', label: 'Barada' },
  { ref: '26', label: 'Bargwill Tomder' },
  { ref: '27', label: 'Baron Papanoida' },
  { ref: '28', label: 'Barriss Offee' },
  { ref: '29', label: 'Baze Malbus' },
  { ref: '30', label: 'Bazine Netal' },
  { ref: '31', label: 'BB-8' },
  { ref: '32', label: 'BB-9E' },
  { ref: '33', label: 'Ben Quadinaros' },
  { ref: '34', label: 'Berch Teller' },
  { ref: '35', label: 'Beru Lars' },
  { ref: '36', label: 'Bib Fortuna' },
  {
    ref: '37',
    label: 'Biggs Darklighter',
  },
  { ref: '38', label: 'Black Krrsantan' },
  { ref: '39', label: 'Bo-Katan Kryze' },
  { ref: '40', label: 'Boba Fett' },
  { ref: '41', label: 'Bobbajo' },
  { ref: '42', label: 'Bodhi Rook' },
  { ref: '43', label: 'Borvo the Hutt' },
  { ref: '44', label: 'Boss Nass' },
  { ref: '45', label: 'Bossk' },
  {
    ref: '46',
    label: 'Breha Antilles-Organa',
  },
  { ref: '47', label: 'Bren Derlin' },
  { ref: '48', label: 'Brendol Hux' },
  { ref: '49', label: 'BT-1' },
];
 
export const MentionKit = [
  MentionPlugin.configure({
    component: MentionElement,
    initialState: {
      triggerPreviousCharPattern: /^$|^[\s"']$/,
    },
    slots: { afterEditable: MentionCombobox },
  }),
];
const
label
=
element.label
??
element.ref;
return (
<EditorElement
{...props}
as="span"
className={cn(
'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
element.children[0].bold === true && 'font-bold',
element.children[0].italic === true && 'italic',
element.children[0].underline === true && 'underline'
)}
attributes={{
...props.attributes,
'data-editor-value': label,
}}
>
{props.children}
{prefix}
{label}
</EditorElement>
);
}
export const BaseMentionKit = [
BaseMentionPlugin.configure({
component: MentionElementStatic,
}),
];
import { BaseMentionPlugin } from 'platejs/mention';
import { type EditorElementProps, EditorElement } from 'platejs/static';
import * as React from 'react';
 
import { cn } from '@/lib/utils';
 
export function MentionElementStatic(
  props: EditorElementProps<typeof BaseMentionPlugin> & {
    prefix?: string;
  }
) {
  const { prefix } = props;
  const { element } = props;
  const label = element.label ?? element.ref;
 
  return (
    <EditorElement
      {...props}
      as="span"
      className={cn(
        'inline-block rounded-md bg-muted px-1.5 py-0.5 align-baseline font-medium text-sm',
        element.children[0].bold === true && 'font-bold',
        element.children[0].italic === true && 'italic',
        element.children[0].underline === true && 'underline'
      )}
      attributes={{
        ...props.attributes,
        'data-editor-value': label,
      }}
    >
      {props.children}
      {prefix}
      {label}
    </EditorElement>
  );
}
 
export const BaseMentionKit = [
  BaseMentionPlugin.configure({
    component: MentionElementStatic,
  }),
];
import { MentionPlugin } from 'platejs/mention/react';
import { createEditor } from 'platejs/react';
 
import {
  MentionCombobox,
  MentionElement,
} from '@/components/editor/mention';
 
export const editor = createEditor({
  plugins: [
    MentionPlugin.configure({
      component: MentionElement,
      initialState: {
        triggerPreviousCharPattern: /^$|^[\s"']$/,
      },
      slots: { afterEditable: MentionCombobox },
    }),
  ],
});
=>
{
tx.plugin(MentionPlugin).insert({
label: item.label,
ref: item.ref,
});
}}
>
{item.label}
</InlineComboboxItem>