@danielivanovz/mention
Headless, a11y-first React mention primitive for textareas. ~11 kB gzip. shadcn-friendly.
A small, focused take on the @-mention pattern — built around the WAI-ARIA combobox-as-substring contract and the <textarea> your users already know how to use. No editor framework. Bring your own design system, or use the default theme.
Try it
Type @ at the start of a word, then ↑/↓ to navigate, Enter to commit, Esc to dismiss while keeping typed text.
Install
bun add @danielivanovz/mentionPeers: react ≥ 19, react-dom ≥ 19. One runtime dep: @floating-ui/react-dom.
Usage
import { Mention } from "@danielivanovz/mention";
import "@danielivanovz/mention/styles.css";
const users = [
{ id: 1, name: "Daniel" },
{ id: 2, name: "Daria" },
{ id: 3, name: "Marcus" },
];
export function MessageInput() {
return (
<Mention.Root
items={users}
getKey={(u) => u.id}
getLabel={(u) => u.name}
onSelect={(u) => console.log("picked", u)}
>
<Mention.Input aria-label="Message" />
<Mention.Popover>
<Mention.List>
{(u) => <Mention.Item value={u}>{u.name}</Mention.Item>}
</Mention.List>
<Mention.Empty>No people found</Mention.Empty>
</Mention.Popover>
</Mention.Root>
);
}That's the whole library.
Why
- A11y by default. Permanent
role="combobox"on the textarea, full APG contract:aria-expanded,aria-controls,aria-activedescendant,aria-autocomplete="list". Mid-word@(email patterns) is suppressed. - Textarea-native. Uses the
<textarea>you already render — no contenteditable, no editor framework. Copy/paste, undo/redo, IME, mobile autocorrect — all "just work". - Caret-anchored popover. Floating UI virtual element + vendored caret-position math; the popover follows the
@as the user types. - shadcn-friendly. Default CSS uses shadcn's CSS variables (
--popover,--accent, …). Drop in, or passunstyledand bring your own. - Small. Well under a 12 kB gzip ceiling, enforced in CI.
Where to next
- API reference — every prop, type, and escape-hatch field, with defaults.
- Recipes — async fetchers, custom rendering, controlled forms, styling, i18n & RTL.
- Accessibility — the combobox-as-substring contract, AT matrix, documented axe exceptions.
- Troubleshooting — common pitfalls (mid-word triggers, key warnings, popover drift).
- Internals — caret-anchoring math, ARIA contract under the hood.
Status
v0.1 — single trigger, <textarea> only. Multi-trigger and contenteditable land in v0.2; the public types are already locked.