Mention
Insert mentions for users, pages, or any reference with @
Slash Command
Quick access to editor commands and blocks with /
Emoji
Insert emojis with autocomplete using :
Features
- Utilities for creating trigger-based combobox functionality
- Configurable trigger characters and patterns
- Keyboard navigation and selection handling
Create a Combobox Plugin
Installation
pnpm add @platejs/combobox
Create Input Plugin
First, create an input plugin that will be inserted when the trigger is activated:
import { createSlatePlugin } from 'platejs';
const TagInputPlugin = createSlatePlugin({
key: 'tag_input',
editOnly: true,
node: {
isElement: true,
isInline: true,
isVoid: true,
},
});
Create Main Plugin
Create your main plugin using withTriggerCombobox
:
import { createTSlatePlugin, type PluginConfig } from 'platejs';
import {
type TriggerComboboxPluginOptions,
withTriggerCombobox
} from '@platejs/combobox';
type TagConfig = PluginConfig<'tag', TriggerComboboxPluginOptions>;
export const TagPlugin = createTSlatePlugin<TagConfig>({
key: 'tag',
node: { isElement: true, isInline: true, isVoid: true },
options: {
trigger: '#',
triggerPreviousCharPattern: /^\s?$/,
createComboboxInput: () => ({
children: [{ text: '' }],
type: 'tag_input',
}),
},
plugins: [TagInputPlugin],
}).overrideEditor(withTriggerCombobox);
node.isElement
: Defines this as an element node (not text)node.isInline
: Makes the tag element inline (not block)node.isVoid
: Prevents editing inside the tag elementoptions.trigger
: Character that triggers the combobox (in this case#
)options.triggerPreviousCharPattern
: RegExp pattern that must match the character before the trigger./^\s?$/
allows the trigger at the start of a line or after whitespaceoptions.createComboboxInput
: Function that creates the input element node when the trigger is activated
Create Component
Create the input element component using InlineCombobox
:
import { PlateElement, useFocused, useReadOnly, useSelected } from 'platejs/react';
import {
InlineCombobox,
InlineComboboxContent,
InlineComboboxEmpty,
InlineComboboxInput,
InlineComboboxItem,
} from '@/components/ui/inline-combobox';
import { cn } from '@/lib/utils';
const tags = [
{ id: 'frontend', name: 'Frontend', color: 'blue' },
{ id: 'backend', name: 'Backend', color: 'green' },
{ id: 'design', name: 'Design', color: 'purple' },
{ id: 'urgent', name: 'Urgent', color: 'red' },
];
export function TagInputElement({ element, ...props }) {
return (
<PlateElement as="span" {...props}>
<InlineCombobox element={element} trigger="#">
<InlineComboboxInput />
<InlineComboboxContent>
<InlineComboboxEmpty>No tags found</InlineComboboxEmpty>
{tags.map((tag) => (
<InlineComboboxItem
key={tag.id}
value={tag.name}
onClick={() => {
// Insert actual tag element
editor.tf.insertNodes({
type: 'tag',
tagId: tag.id,
children: [{ text: tag.name }],
});
}}
>
<span
className={`w-3 h-3 rounded-full bg-${tag.color}-500 mr-2`}
/>
#{tag.name}
</InlineComboboxItem>
))}
</InlineComboboxContent>
</InlineCombobox>
{props.children}
</PlateElement>
);
}
export function TagElement({ element, ...props }) {
const selected = useSelected();
const focused = useFocused();
const readOnly = useReadOnly();
return (
<PlateElement
{...props}
className={cn(
'inline-block rounded-md bg-primary/10 px-1.5 py-0.5 align-baseline text-sm font-medium text-primary',
!readOnly && 'cursor-pointer',
selected && focused && 'ring-2 ring-ring'
)}
attributes={{
...props.attributes,
contentEditable: false,
'data-slate-value': element.value,
}}
>
#{element.value}
{props.children}
</PlateElement>
);
}
Add to Editor
import { createPlateEditor } from 'platejs/react';
import { TagPlugin, TagInputPlugin } from './tag-plugin';
import { TagElement, TagInputElement } from './tag-components';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
TagPlugin.configure({
options: {
triggerQuery: (editor) => {
// Disable in code blocks
return !editor.api.some({ match: { type: 'code_block' } });
},
},
}).withComponent(TagElement),
TagInputPlugin.withComponent(TagInputElement),
],
});
options.triggerQuery
: Optional function to conditionally enable/disable the trigger based on editor state
Examples
Options
TriggerComboboxPluginOptions
Configuration options for trigger-based combobox plugins.
createComboboxInput (trigger: string) => TElement
Function to create the input node when trigger is activated.
trigger RegExp | string[] | string
Character(s) that trigger the combobox. Can be:
- A single character (e.g. '@')
- An array of characters
- A regular expression
triggerPreviousCharPattern optional RegExp
Pattern to match the character before trigger.
- Example:
/^\s?$/
matches start of line or space
- Example:
triggerQuery optional (editor: SlateEditor) => boolean
Custom query function to control when trigger is active.
Hooks
useComboboxInput
Hook for managing combobox input behavior and keyboard interactions.
ref RefObject<HTMLElement>
Reference to the input element.
autoFocus optional boolean
Auto focus the input when mounted.
- Default:
true
- Default:
cancelInputOnArrowLeftRight optional boolean
Cancel on arrow keys.
- Default:
true
- Default:
cancelInputOnBackspace optional boolean
Cancel on backspace at start.
- Default:
true
- Default:
cancelInputOnBlur optional boolean
Cancel on blur.
- Default:
true
- Default:
cancelInputOnDeselect optional boolean
Cancel when deselected.
- Default:
true
- Default:
cancelInputOnEscape optional boolean
Cancel on escape key.
- Default:
true
- Default:
cursorState optional ComboboxInputCursorState
Current cursor position state.
forwardUndoRedoToEditor optional boolean
Forward undo/redo to editor.
- Default:
true
- Default:
onCancelInput optional (cause: CancelComboboxInputCause) => void
Callback when input is cancelled.
useHTMLInputCursorState
Hook for tracking cursor position in an HTML input element.