Combobox
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 :
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
'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
const editor = usePlateEditor({
plugins: EditorKit,
value: DEMO_VALUES[id],
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
const editor = usePlateEditor({
plugins: EditorKit,
value: DEMO_VALUES[id],
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
const editor = usePlateEditor({
plugins: EditorKit,
value: DEMO_VALUES[id],
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
Options
TriggerComboboxPluginOptions
Configuration options for trigger-based combobox plugins.
- A single character (e.g. '@')
- An array of characters
- A regular expression
- Example:
/^\s?$/
matches start of line or space
Function to create the input node when trigger is activated.
Character(s) that trigger the combobox. Can be:
Pattern to match the character before trigger.
Custom query function to control when trigger is active.
Hooks
useComboboxInput
Hook for managing combobox input behavior and keyboard interactions.
- Default:
true
- Default:
true
- Default:
true
- Default:
true
- Default:
true
- Default:
true
- Default:
true
Reference to the input element.
Auto focus the input when mounted.
Cancel on arrow keys.
Cancel on backspace at start.
Cancel on blur.
Cancel when deselected.
Cancel on escape key.
Current cursor position state.
Forward undo/redo to editor.
Callback when input is cancelled.
useHTMLInputCursorState
Hook for tracking cursor position in an HTML input element.