Introduction

Multi-trigger inline mentions for React, Vue, Svelte, and Angular. Built on a framework-agnostic core.

What is @skyastrall/mentions?

A production-grade mentions input that supports @users, #tags, /commands, and custom triggers — each with its own data source, color, and configuration.

It ships as a core engine and thin framework adapters:

Three API Layers

Pick the level of control you need:

1. Drop-in Component

One import, one component, works out of the box.

<Mentions
  triggers={[{ char: "@", data: users }]}
  onChange={(markup, plainText) => {}}
/>
<Mentions
  :triggers="[{ char: '@', data: users }]"
  v-model="markup"
/>
<Mentions
  triggers={[{ char: "@", data: users }]}
  onChange={(markup, plainText) => {}}
/>
<sa-mentions
  [triggers]="[{ char: '@', data: users }]"
  [(ngModel)]="markup"
/>

2. Compound Components

Control the layout. The library handles state, ARIA, and keyboard navigation. (Angular is component-first — it ships a single <sa-mentions>; for custom layout drop to the headless bridge below.)

<Mentions triggers={triggers}>
  <Mentions.Editor placeholder="Type @..." />
  <Mentions.Portal>
    <Mentions.List>
      <Mentions.Item render={({ item }) => <UserCard user={item} />} />
    </Mentions.List>
  </Mentions.Portal>
</Mentions>
<Mentions :triggers="triggers">
  <MentionsEditor placeholder="Type @..." />
  <MentionsPortal>
    <MentionsList>
      <MentionsItem v-slot="{ item }">
        <UserCard :user="item" />
      </MentionsItem>
    </MentionsList>
  </MentionsPortal>
</Mentions>
<Mentions {triggers}>
  <MentionsEditor placeholder="Type @..." />
  <MentionsPortal>
    <MentionsList>
      <MentionsItem>
        {#snippet children({ item })}
          <UserCard user={item} />
        {/snippet}
      </MentionsItem>
    </MentionsList>
  </MentionsPortal>
</Mentions>
<!-- Angular renders a complete component; configure it via inputs -->
<sa-mentions
  [triggers]="triggers"
  [(ngModel)]="markup"
  placeholder="Type @..."
/>

<!-- For full layout control, drive the headless bridge yourself (below). -->

3. Headless Hook / Composable

Full control. You get props, state, and helpers — render whatever you want.

const { inputProps, listProps, getItemProps, editorRef, isOpen, items } =
  useMentions({ triggers });
const { aria, editorRef, isOpen, items, handleInput, handleKeyDown } =
  useMentions({ triggers });
const api = useMentions({ triggers });
// api.isOpen, api.items, api.aria, api.handleInput, ...
import { DestroyRef, inject } from "@angular/core";
import { MentionsControllerBridge } from "@skyastrall/mentions-angular";

// state() is a signal; drive everything through the controller.
bridge = new MentionsControllerBridge({ triggers }, inject(DestroyRef));
// bridge.state().items, .status, .query ...
// bridge.controller.handleInputChange(markup, plainText, cursor);

Key Features

Next Steps

Installation or Quick Start.