useMentions Hook
The headless hook. Full control over rendering.
Import
import { useMentions } from "@skyastrall/mentions-react"; import { useMentions } from "@skyastrall/mentions-vue"; import { useMentions } from "@skyastrall/mentions-svelte"; Options
| Prop | Type | Default | Description |
|---|---|---|---|
triggers | TriggerConfig[] | — | Trigger configurations (required) |
value | string | — | Controlled markup value |
defaultValue | string | — | Initial markup for uncontrolled mode |
onChange | (markup, plainText) => void | — | Called on every content change |
onSelect | (item, trigger) => void | — | Called when a mention is selected |
onRemove | (item, trigger) => void | — | Called when a mention is removed |
onQueryChange | (query, trigger) => void | — | Called when the search query changes |
onOpen | (trigger) => void | — | Called when the dropdown opens |
onClose | () => void | — | Called when the dropdown closes |
onError | (error) => void | — | Called on async fetch errors |
ghostText | string | — | Dimmed inline completion text |
onAcceptGhostText | () => void | — | Called when ghost text is accepted via Tab |
Return Value
| Prop | Type | Default | Description |
|---|---|---|---|
editorRef | RefObject<HTMLDivElement | null> | — | Attach to your contenteditable div |
inputProps | object | — | ARIA + event handlers — spread onto the editor div |
listProps | object | — | ARIA attributes for the suggestion list |
getItemProps | (index) => object | — | ARIA + handlers for each item |
isOpen | boolean | — | Whether the dropdown is visible |
isLoading | boolean | — | Whether an async fetch is in progress |
items | MentionItem[] | — | Current suggestion items |
query | string | — | Current search query text |
highlightedIndex | number | — | Highlighted item (-1 = none) |
activeTrigger | string | null | — | Active trigger character |
caretPosition | CaretPosition | null | — | Caret viewport coordinates |
state | MentionState | — | Full state machine state (status, isComposing, etc.) |
markup | string | — | Current markup string |
plainText | string | — | Current plain text |
mentions | MentionItem[] | — | All active mentions |
ghostText | string | undefined | — | Current ghost text value |
handleInput | () => void | — | Wire to onInput on your contenteditable div |
clear | () => void | — | Clears all content |
focus | () => void | — | Focuses the editor |
insertTrigger | (trigger) => void | — | Inserts trigger and opens dropdown |
inputProps includes ARIA combobox attributes (role="combobox", aria-expanded, aria-activedescendant, etc.) plus onKeyDown, onBlur, onCompositionStart, and onCompositionEnd handlers. Spread it onto your contenteditable div.
Basic Example
import { useMentions } from "@skyastrall/mentions-react";
const users = [
{ id: "1", label: "Alice" },
{ id: "2", label: "Bob" },
];
function CustomEditor() {
const {
editorRef, inputProps, listProps, getItemProps,
isOpen, items, highlightedIndex, caretPosition,
} = useMentions({
triggers: [{ char: "@", data: users }],
onChange: (markup, plainText) => console.log(markup),
});
return (
<div data-mentions="" style={{ position: "relative" }}>
<div
ref={editorRef}
contentEditable="plaintext-only"
data-mentions-editor=""
{...inputProps}
style={{ outline: "none", whiteSpace: "pre-wrap", padding: 12 }}
/>
{isOpen && items.length > 0 && (
<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)}
style={{
padding: "8px 12px",
background: i === highlightedIndex ? "#f1f5f9" : "white",
cursor: "pointer",
}}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
} The editor needs
data-mentions=""on a parent anddata-mentions-editor=""on itself for blur handling and placeholder CSS to work correctly.