Mention Input
ProContenteditable input with @mention autocomplete — a cursor-anchored suggestion dropdown, keyboard navigation and styled mention chips. Supports single- or multi-line entry and multi-trigger mode (@ people, # topics, / commands).
import { SvMentionComponent } from '@styloviz-pro/forms';Demo & usage
Basic usage
ProUsage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Single-line with # trigger
ProMulti-trigger — @ people, # topics, / commands (Pro)
ProPass a triggers array; each entry has its own char, suggestion set and optional chip colour. triggerSelected tells you which trigger fired.
When to use
- Letting users reference people or entities inline (comments, chat, task notes).
- You want @-style autocomplete that anchors to the caret.
- For a standalone tag field (not inline in prose), use Tag Input.
Async states
- loading
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| suggestions | MentionSuggestion[] | [] | List of mention candidates. |
| triggerChar | string | '@' | Character that triggers the dropdown (single-trigger mode). |
| triggers | readonly MentionTrigger[] | [] | Multi-trigger configuration (Pro). When non-empty, this takes precedence over `triggerChar`/`suggestions`: each entry defines a trigger character, its own suggestion set, and optional chip styling. |
| placeholder | string | 'Type @ to mention someone…' | Placeholder text. |
| multiLine | boolean | false | Multi-line mode (expand with content). |
| maxSuggestions | number | 8 | Maximum items shown in the dropdown. |
| disabled | boolean | false | Disabled state. |
| value | string | '' | Plain-text content. Two-way bindable with `[(value)]`. |
| serverSide | boolean | false | Let the server do the matching. The supplied `suggestions` are shown exactly as given. |
| debounceMs | number | 200 | Delay before `queryChange` fires, in ms. `0` emits immediately. |
| minChars | number | 0 | Characters after the trigger before a lookup is sent. `0` looks up as soon as the trigger is typed, which is what most directories want. |
| loading | boolean | false | True while suggestions are being fetched. |
| error | SvDataError | string | null | null | Failure loading suggestions, rendered inside the panel. |
| noResultsText | string | 'No matches' | Copy when the lookup returned nothing. |
| errorMessage | string | '' | Client-side validation message. |
| serverError | SvServerError | null | A rejection reported by the server about this value. Self-expiring. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| mentionSelected | MentionSuggestion | Emitted when a mention is selected. |
| triggerSelected | { trigger: string; suggestion: MentionSuggestion } | Emitted alongside `mentionSelected`, carrying the trigger character that produced the mention — useful in multi-trigger mode to tell `@`, `#` and `/` selections apart. |
| valueChange | string | Emitted when the text content changes (pairs with `value` for `[(value)]`). |
| queryChange | MentionQueryEvent | The text typed after the trigger changed — fetch matching people. Carries the trigger character too, because a component with several triggers (`@` for people, `#` for channels) queries different endpoints. Debounced by `debounceMs` and never emitted twice for the same pair. |
| retry | void | The host should re-run the failed lookup. |
| mentionsChange | SvMentionState | The mentions actually in the field, and the ones that no longer say what they mean. AN OUTPUT RATHER THAN ONLY A METHOD, BECAUSE THIS COMPONENT'S VALUE CARRIES NO IDS. `value` is plain text; the ids live only in the DOM. A host binding `[(value)]` and `(mentionSelected)` therefore keeps its recipient list BESIDE the document — and that list is wrong the moment somebody deletes a chip, pastes over one, or edits its text. There is no reconciling it from the value, so the component has to volunteer the truth. `sv-rich-text-editor` exposes methods instead and can afford to: its value is the markup, so the ids are in the thing the host already persists. Emitted only when the state CHANGES, not per keystroke. A mention field fires `input` on every character, and a host that re-runs a permission check each time would be doing it thousands of times per message. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — it just saves you writing the debounce, the request guard and the retry policy by hand.
Template
ProBound to the names this component actually exposes.
Host class
ProOne controller handles debounce, request-guarding, cancellation and retry.
- `queryChange` carries an `SvDataQuery` — page, page size, sorts, filters, search and cursor in one object. One handler covers every way the user can change what they are asking for.
- `serverSide` switches **off** the built-in filtering, sorting and paging. Leave it false and the component works entirely from the array you pass — which is what you want for a few hundred rows.
- `debounceMs` (default 300) collapses a burst of typing into one request. Set it to 0 only when the query is driven by clicks.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Provide `suggestions` (`MentionSuggestion[]`) and change the `triggerChar` (e.g. `@`, `#`, `/`).
- For several inline autocompletes in one field, pass `triggers` (`MentionTrigger[]`) — each has its own `char`, `suggestions`, optional `chipClass` and `label`; the nearest trigger to the caret wins.
- Toggle `multiLine` for a textarea-style field and cap results with `maxSuggestions`.
- Listen to `mentionSelected` for the chosen entity, `triggerSelected` for the `{ trigger, suggestion }` pair, and `valueChange` for the raw text.
- Bind the plain-text content with `[(value)]`, or use `[formControl]` / `[(ngModel)]` — the editor implements `ControlValueAccessor` and hydrates programmatic values when not focused.
- Responsive: the suggestion popup is anchored to the caret and its width is capped to the viewport, so it stays fully on-screen on narrow devices.
Best practices
- Do: Cap suggestions with `maxSuggestions` so the dropdown stays scannable.
- Do: Resolve `mentionSelected` to a stable id, not just the display name.
- Tip: Change `triggerChar` (@, #, /) to match the entity type being referenced.
Accessibility
- The editor is `role="textbox"` with `aria-autocomplete="list"`, `aria-expanded`, `aria-controls` and `aria-activedescendant` tracking the highlighted suggestion (ids are generated per instance, so multiple fields never collide).
- Arrow keys move, Enter/Tab insert, Esc closes without losing the text; the suggestion popup is a `role="listbox"` of `role="option"` rows.
- Inserted mentions render as styled chips kept in the text flow for screen readers.
Feedback on Mention Input
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.