Headless Hook
Full control over rendering. You own the DOM, the library owns the logic.
When to Use
- You need a custom editor element (not the default contenteditable div)
- You want to integrate mentions into an existing editor
- You need complete control over the dropdown positioning and rendering
Minimal Example
import { useMentions } from "@skyastrall/mentions-react";
const users = [
{ id: "1", label: "Alice" },
{ id: "2", label: "Bob" },
];
function Editor() {
const {
editorRef, inputProps, listProps, getItemProps,
isOpen, items, highlightedIndex, caretPosition, handleInput,
} = useMentions({
triggers: [{ char: "@", data: users }],
});
return (
<div data-mentions="" style={{ position: "relative" }}>
<div
ref={editorRef}
contentEditable="plaintext-only"
onInput={handleInput}
data-mentions-editor=""
{...inputProps}
/>
{isOpen && (
<ul {...listProps} style={{
position: "fixed",
top: (caretPosition?.top ?? 0) + (caretPosition?.height ?? 0) + 4,
left: caretPosition?.left ?? 0,
}}>
{items.map((item, i) => (
<li key={item.id} {...getItemProps(i)}>
{item.label}
</li>
))}
</ul>
)}
</div>
);
} Important Attributes
data-mentions=""on a wrapper div — needed for blur detectiondata-mentions-editor=""on the editor — needed for placeholder CSSonInput={handleInput}— triggers state derivation from DOM
Reading Values
const { markup, plainText, mentions } = useMentions({ triggers });
// markup: "@[Alice](1) hello"
// plainText: "@Alice hello"
// mentions: [{ id: "1", label: "Alice" }] Programmatic Actions
const { focus, clear, insertTrigger } = useMentions({ triggers });
focus(); // Focus the editor
clear(); // Clear all content
insertTrigger("@"); // Insert @ and open dropdown