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 and data-mentions-editor="" on itself for blur handling and placeholder CSS to work correctly.