Skip to Content
⭐️ Leave a star →
ComponentsMention

Mention

Inline @-mention primitive: a combobox that tracks the caret inside a textarea, filters options as you type, and inserts the chosen token. The trigger character, options, and filtering are all configurable.

Features

Detects the trigger token at the caret and anchors a listbox to it.
Configurable trigger character (default @) and custom filter predicate.
Full keyboard navigation: arrows to move, Enter/Tab to select, Escape to dismiss.
Render-prop items with active/disabled state via data attributes.
Controlled or uncontrolled text value.
Implements the WAI-ARIA combobox pattern with aria-activedescendant.

Example

You
import { Mention } from '@wire-ui/react' const people = [ { id: 1, label: 'Ada Lovelace' }, { id: 2, label: 'Alan Turing' }, { id: 3, label: 'Grace Hopper' }, ] <Mention.Root options={people} className="relative"> <Mention.Input placeholder="Type @ to mention someone…" /> <Mention.Content> <Mention.Items> {({ option }) => <div>{option.label}</div>} </Mention.Items> <Mention.Empty>No people found</Mention.Empty> </Mention.Content> </Mention.Root>

Styling

Mention.Root must be positioned (relative) so the Content listbox can anchor to the caret — it is absolutely positioned at the computed coordinates. Each option exposes data-active (the highlighted item) and data-disabled, so you can style hover/active and disabled states with attribute selectors.

<div className=" data-[active]:bg-black data-[active]:text-white data-[disabled]:opacity-40 data-[disabled]:cursor-not-allowed "> {option.label} </div>

Using data attributes

Mention.Content sets data-state="open" (it only renders while open). Each rendered option sets data-active when highlighted and data-disabled when the option is disabled. Mention.Root sets data-disabled when the whole control is disabled.

/* Highlighted option */ [data-active] { background: #000; color: #fff; } /* Disabled option */ [data-disabled] { opacity: 0.4; cursor: not-allowed; }

Root props

PropTypeDefaultDescription
optionsMentionOption[]requiredOptions shown when the trigger is active
valuestringControlled text value
defaultValuestring''Initial text value (uncontrolled)
onChange(value: string) => voidCalled whenever the text changes
triggerstring'@'Character that opens the menu
filter(option: MentionOption, query: string) => booleanlabel includes queryPredicate used to filter options against the current query
onSelect(option: MentionOption) => voidCalled when an option is chosen
appendSpacebooleantrueAppend a space after the inserted mention
disabledbooleanfalseDisable the whole control

Input props

Accepts all native <textarea> attributes (e.g. rows, placeholder, aria-label). The value and disabled are managed by Mention.Root.

Items props

PropTypeDescription
children(props: MentionItemRenderProps) => ReactNodeRender function called for each filtered option

MentionItemRenderProps is { option: MentionOption; active: boolean; index: number }.

Content / Empty props

Both accept all native <div> attributes. Content renders only while the menu is open; Empty renders only while the menu is open and no options match.

MentionOption

FieldTypeDescription
idstring | numberUnique key
labelstringDisplay text — also inserted after the trigger by default
valuestringOptional value inserted instead of label
disabledbooleanSkip during keyboard navigation and ignore on click

Data attributes

AttributeElementValues
data-stateContent"open"
data-activeOptionPresent on the highlighted option
data-disabledRoot, OptionPresent when disabled

Input also sets aria-autocomplete="list", aria-expanded, aria-controls, and aria-activedescendant. Content sets role="listbox"; each option sets role="option" and aria-selected.

Accessibility

  • Implements the WAI-ARIA combobox/listbox pattern. The textarea owns the listbox via aria-controls and tracks the highlighted option via aria-activedescendant.
  • The trigger only opens at the start of the text or after whitespace, so addresses like a@b.com do not trigger the menu.
  • Disabled options are skipped during arrow-key navigation and cannot be selected.

Keyboard Interactions

KeyDescription
ArrowDownMove to the next option (skips disabled).
ArrowUpMove to the previous option (skips disabled).
Enter / TabSelects the highlighted option.
EscapeDismisses the menu for the current token.
Last updated on

MIT License © 2026 wire-ui

Mention – Wire UI