InputsReact
A text field that suggests people, channels or tags after a trigger character.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<MentionInput.Root>
<MentionInput.Input />
<MentionInput.Portal>
<MentionInput.Positioner>
<MentionInput.Popup>
<MentionInput.Empty />
<MentionInput.List>
<MentionInput.Collection>
<MentionInput.Item />
</MentionInput.Collection>
</MentionInput.List>
</MentionInput.Popup>
</MentionInput.Positioner>
</MentionInput.Portal>
</MentionInput.Root>
// Not in this example: MentionInput.Arrow, MentionInput.Group, MentionInput.GroupLabel, MentionInput.Row, MentionInput.SeparatorExamples
Triggers
A message box with trigger @ and #: onQueryChange swaps the items between people and channels, and names the List for each.
API reference
60 props across 14 parts, in React syntax. className, style, render are listed once, under Shared props.
ArrowMentionInput.Arrow
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-uncentered | Present when the arrow is uncentered. |
CollectionMentionInput.Collection
Renders filtered list items. If rendering a flat list, pass a function child to the List component instead, which implicitly wraps it. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
childrenrequired | (item: any, index: number) => React.ReactNode | — |
EmptyMentionInput.Empty
GroupMentionInput.Group
| Prop | Type | Default |
|---|---|---|
itemsItems to be rendered within this group. | readonly any[] | — |
GroupLabelMentionInput.GroupLabel
InputMentionInput.Input
The text field the mentions are typed into, on the Combobox store's reference props., or a single-line <input> with render={<input />}. Renders a <textarea>.
| Prop | Type | Default |
|---|---|---|
disabledWhether the component should ignore user interaction. | boolean | false |
ItemMentionInput.Item
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
onClickAn optional click handler for the item when selected. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
indexThe index of the item in the list. | number | — |
valueA unique value that identifies this item. | any | null |
disabledWhether the component should ignore user interaction. | boolean | false |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
ListMentionInput.List
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | ((item: any, index: number) => React.ReactNode) | — |
PopupMentionInput.Popup
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the popup is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the popup is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-anchor-hidden | Present when the anchor is hidden. |
data-empty | Present when the items list is empty. |
PortalMentionInput.Portal
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
PositionerMentionInput.Positioner
Positions the popup against the trigger character in the field. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
alignHow to align the popup relative to the specified side. | Align | 'start' |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-anchor-hidden | Present when the anchor is hidden. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-empty | Present when the items list is empty. |
| CSS variable | Description |
|---|---|
--available-width | The available width between the trigger and the edge of the viewport. |
--available-height | The available height between the trigger and the edge of the viewport. |
--anchor-width | The anchor's width. |
--anchor-height | The anchor's height. |
--transform-origin | The coordinates that this element is anchored to. Used for animations and transitions. |
RootMentionInput.Root
Groups all parts of the mention input: a text field that suggests items after a trigger character.; the status region is rendered after its children. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
valueThe text of the field. | string | — |
defaultValueThe text of the field when it's initially rendered. | string | — |
onValueChangeEvent handler called when the text of the field changes. | ((value: string, eventDetails: MentionInputRootChangeEventDetails) => void) | — |
triggerThe characters that start a query: one character, or a list of characters or of | string | readonly (string | MentionInputTrigger)[] | '@' |
insertSuffixThe text inserted after an item's text, left out when whitespace already follows the caret. | string | ' ' |
itemToInsertTextConverts an item to the text that replaces the trigger and the query. | ((itemValue: ItemValue, trigger: string) => string) | — |
itemToStringValueConverts an item to the string it is filtered and inserted by. | ((itemValue: ItemValue) => string) | — |
itemsThe items to be displayed in the list. | readonly ItemValue[] | readonly Group<ItemValue>[] | — |
filteredItemsFiltered items to display in the list, used instead of filtering items by the query. | readonly ItemValue[] | readonly Group<ItemValue>[] | — |
filterFilter function used to match items against the query. | ((item: ItemValue, query: string, itemToString?: ((item: ItemValue) => string) | undefined) => boolean) | null | — |
limitThe maximum number of items to display in the list. | number | -1 |
localeThe locale used by the default filter. | Intl.LocalesArgument | — |
onQueryChangeEvent handler called when the query the caret is in changes, or ends (null). | ((query: MentionInputQuery | null, eventDetails: MentionInputRootChangeEventDetails) => void) | — |
onItemInsertEvent handler called before an item is inserted. | ((itemValue: ItemValue, eventDetails: MentionInputInsertEventDetails) => void) | — |
onOpenChangeEvent handler called when the popup is opened or closed. | ((open: boolean, eventDetails: MentionInputRootOpenChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the popup is opened or closed. | ((open: boolean) => void) | — |
onItemHighlightedCallback fired when an item is highlighted or unhighlighted. | ((highlightedValue: ItemValue | undefined, eventDetails: AriaCombobox.HighlightEventDetails) => void) | — |
autoHighlightWhether the first enabled item is highlighted when the popup opens and when the list | boolean | true |
keepHighlightWhether the highlighted item should be preserved when the pointer leaves the list. | boolean | false |
highlightItemOnHoverWhether moving the pointer over items should highlight them. | boolean | true |
loopFocusWhether to loop keyboard focus back to the first item when the end of the list is reached. | boolean | true |
gridWhether the items are arranged in a grid. | boolean | false |
virtualizedWhether the items are being externally virtualized. | boolean | false |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the text. | boolean | false |
requiredWhether the field must be filled before the owning form is submitted. | boolean | false |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the field. | string | — |
idThe id of the field. | string | — |
inputRefA ref to the field element. | React.Ref<HTMLInputElement | HTMLTextAreaElement> | — |
labelsThe texts of the status region, merged over the English defaults. | Partial<MentionInputLabels> | — |
actionsRefA ref to imperative actions. | React.RefObject<MentionInputRootActions | null> | — |
RowMentionInput.Row
SeparatorMentionInput.Separator
| Prop | Type | Default |
|---|---|---|
orientationThe orientation of the separator. | Orientation | 'horizontal' |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State & State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State & State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State & State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| ArrowDownArrowUp | While open, moves the highlight, skipping disabled items and looping | Input |
| Enter | Puts the highlighted item in place of the query | Input |
| Enter | With nothing highlighted, is the field's: a line break, and the popup closes | Input |
| Escape | Closes the popup; typing on in the same query does not open it again | Input |
| Tab | Never puts an item in; focus moves on and the popup closes | Input |
Is the field's: the arrows move the caret, Enter adds a line | Input |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Input | on a <textarea>, no role (it stays a textbox), aria-autocomplete="list" and aria-haspopup="listbox" | a multi-line field is still read and edited as one, and says it has suggestions |
| Input | on an <input>, role="combobox" with aria-expanded | a single-line field is read as an editable combobox that says whether the popup is shown |
| Input | aria-controls to the List while the popup is open, aria-activedescendant to the highlighted item | the highlight is read while focus stays in the field |
| List | role="listbox", named by the app (aria-label) | the suggestions are announced as a list with a name, such as "Teammates" |
| Item | role="option", no aria-selected | each suggestion is read as an option; nothing is remembered as a selection |
| Root | a polite status region after its children: "5 suggestions", "No suggestions", then "@alice inserted" | the count and the insertion are announced without moving focus; labels translates them |
Focus order
- Focus stays in the Input the whole time; the arrows move a highlight in the list, not focus.
- Nothing in the popup is a Tab stop, and a press on an item keeps focus and the caret in the field.
Tabmoves on to the next element; the blur closes the popup.