Syncfusion AI Assistant

How can I help you?

Integrate Mention Component into the Angular Rich Text Editor

6 Apr 202618 minutes to read

The Mention Component used to display a list of items that the users can select or tag from the list suggested. When integrated with the Rich Text Editor, it provides inline recommendations within the editable area, enabling seamless mentioning and tagging.

Prerequisites

Before proceeding, complete the base Rich Text Editor setup described in the Getting Started guide. The guide covers Angular CLI setup, package installation, CSS imports, module injection, and basic editor markup: Getting Started with Angular Rich Text Editor.

Key features

  • Provides inline mention suggestions directly inside the editable area
  • Supports both local arrays and remote data sources for loading mention suggestions
  • Allows customizing the popup appearance using item templates and display templates
  • Offers flexible configuration options such as minLength, suggestionCount, and allowSpaces to control search behavior and suggestion filtering

Set up the Mention Component

Install the Syncfusion Mention package using the following command:

npm install @syncfusion/ej2-angular-dropdowns

Configure the Mention Component for the Rich Text Editor

Follow the steps below to set up the Mention component inside the Syncfusion Angular Rich Text Editor.

Step 1: Configure Mention Target

Link the Mention component to the Rich Text Editor’s editable area by setting the target property.
The Rich Text Editor automatically appends _rte-edit-view to its editable element ID.
This ensures that the Mention popup appears at the correct cursor position inside the editor.

<ejs-mention target="#YourEditorID_rte-edit-view"></ejs-mention>

Step 2: Configure Data Source

Provide the list of items that should appear in the suggestion popup.

public userData = [
  { id: 1, name: 'Andrew Fuller' },
  { id: 2, name: 'Anne Dodsworth' }
];

Bind the data source to the Mention component:

<ejs-mention [dataSource]="userData"></ejs-mention>

Step 3: Configure Fields

Map your data model fields to define how items are displayed and what value is inserted into the editor content.

public fields = { text: 'name', value: 'id' };
<ejs-mention [fields]="fields"></ejs-mention>

Step 4: Configure Item Template

Use the itemTemplate to customize how each suggestion item is displayed in the Mention popup. This allows you to show additional details such as avatar initials, name, and email in a structured layout.

<ejs-mention #editorMention>
    <ng-template #itemTemplate let-data>
        <div class="editor-mention-item-template">
            <div class="em-header">
                <div class="em-avatar" [ngStyle]="{ 'background-color': data.bgColor, 'color': data.color}">
                    <div class="em-initial"></div>
                </div>
            </div>
            <div class="em-content">
                <div class="em-name"></div>
                <div class="em-email"></div>
            </div>
        </div>
    </ng-template>
</ejs-mention>

Step 5: Configure Display Template

Use displayTemplate to customize how the selected mention appears when inserted into the Rich Text Editor content.

<ng-template #displayTemplate let-data>
    <a href=mailto: title=>&#64;</a>
</ng-template>

Step 6: Configure SuffixText

It is suggested to use the suffixText property to add a space after the inserted mention, which helps maintain a smooth typing flow in the Rich Text Editor.

<ejs-mention suffixText="&nbsp;"></ejs-mention>

Example: Integrate Mention with Syncfusion Rich Text Editor (app.ts)

import { MentionModule } from '@syncfusion/ej2-angular-dropdowns';
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { RichTextEditorModule, ToolbarService, HtmlEditorService, ImageService, LinkService, QuickToolbarService, TableService, PasteCleanupService } from '@syncfusion/ej2-angular-richtexteditor';
import { NgStyle } from '@angular/common';

@Component({
    imports: [RichTextEditorModule, MentionModule, NgStyle],
    standalone: true,
    selector: 'app-root',
    styleUrl: 'app.css',
    encapsulation: ViewEncapsulation.None,
    template: `
  <div>
    <ejs-richtexteditor
      id="mention_integration"
      placeholder="Type @ and tag the name"
    >
      <ng-template #valueTemplate>
        <p>
          Hello
          <span contenteditable="false" class="e-mention-chip"
            ><a href="mailto:[email protected]" title="[email protected]"
              >Maria</a
            ></span
          >&#8203;
        </p>
        <p>
        The suggestion list displays only 5 items when typing the &#64; character, as the data source contains a large set of entries
        </p>
      </ng-template>
    </ejs-richtexteditor>
  </div>
  <div>
   <ejs-mention #editorMention [dataSource]='data' [fields]='fieldsData' [allowSpaces]="true"
        popupWidth='250px' popupHeight='200px' sortOrder='Ascending' target='#mention_integration_rte-edit-view' [suffixText]='&nbsp;'>
        <ng-template #displayTemplate let-data>
            <a href=mailto: title=>&#64;</a>
        </ng-template>
        <ng-template #itemTemplate let-data>
            <div class="editor-mention-item-template">
                <div class="em-header">
                    <div class="em-avatar" [ngStyle]="{ 'background-color': data.bgColor, 'color': data.color}">
                        <div class="em-initial"></div>
                    </div>
                </div>
                <div class="em-content">
                    <div class="em-name"></div>
                    <div class="em-email"></div>
                </div>
            </div>
        </ng-template>
    </ejs-mention>
  </div>
`,
    providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, TableService, PasteCleanupService, NgStyle]
})
export class App {
    public data: { [key: string]: Object }[] = [
        { name: "Selma Rose", initial: 'SR', email: "[email protected]", color: '#FAFDFF', bgColor: '#01579B' },
        { name: "Maria", initial: 'MA', email: "[email protected]", color: '#004378', bgColor: '#ADDBFF' },
        { name: "Russo Kay", initial: 'RK', email: "[email protected]", color: '#F9DEDC', bgColor: '#8C1D18' },
        { name: "Robert", initial: 'RO', email: "[email protected]", color: '#FFD6F7', bgColor: '#37003A' },
        { name: "Camden Kate", initial: 'CK', email: "[email protected]", color: '#FFFFFF', bgColor: '#464ECF' },
        { name: "Garth", initial: 'GA', email: "[email protected]", color: '#FFFFFF', bgColor: '#008861' },
        { name: "Andrew James", initial: 'AJ', email: "[email protected]", color: '#FFFFFF', bgColor: '#53CA17' },
        { name: "Olivia", initial: 'OL', email: "[email protected]", color: '#FFFFFF', bgColor: '#8C1D18' },
        { name: "Sophia", initial: 'SO', email: "[email protected]", color: '#000000', bgColor: '#D0BCFF' },
        { name: "Margaret", initial: 'MA', email: "[email protected]", color: '#000000', bgColor: '#F2B8B5' },
        { name: "Ursula Ann", initial: 'UA', email: "[email protected]", color: '#000000', bgColor: '#47ACFB' },
        { name: "Laura Grace", initial: 'LG', email: "[email protected]", color: '#000000', bgColor: '#FFE088' },
        { name: "Albert", initial: 'AL', email: "[email protected]", color: '#FFFFFF', bgColor: '#00335B' },
        { name: "William", initial: 'WA', email: "[email protected]", color: '#FFFFFF', bgColor: '#163E02' }
    ];
    public fieldsData: { [key: string]: string } = { text: 'name' };
}
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app.component';
import 'zone.js';

bootstrapApplication(App).catch((err) => console.error(err));
/** Mention template styles **/
.editor-mention-item-template {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
}
.em-content {
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.em-avatar {
    width: 32px;
    height: 32px;
    text-align: center;
    border-radius: 50%;
    font-size: 12px;
    font-weight: 500;
    text-indent: 0px;
    line-height: 13px;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}
.em-name {
    color: rgb(16, 24, 40);
    font-size: 14px;
    font-weight: 400;
    line-height: 14px;
    margin-bottom: 5px;
}
.em-email {
    color: gray;
    font-size: 12px;
    font-weight: 400;
    line-height: 14px;
}
#mention_integration_rte-edit-view_options li {
    padding: 10px;
    height: 60px;
}
@import '../node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind3.css';

Using Remote Data with Mention in Rich Text Editor

You can bind Mention to a remote data source so suggestions are fetched from your server as the user types. Use Syncfusion’s DataManager + Query (with a suitable adaptor such as WebApiAdaptor) as the Mention dataSource.

Install the DataManager package for remote data operations

Install the Syncfusion Data package required for remote data binding:

npm install @syncfusion/ej2-data

Then import the required classes in your component:

import { DataManager, Query, WebApiAdaptor } from '@syncfusion/ej2-data';

Example: Integrate Remote Data With Syncfusion Rich Text Editor (app.ts)

Note: When using DataManager with WebApiAdaptor, the server must return a JSON object containing:

  • result — an array of data items (paged or filtered)
  • count — the total number of records before paging

Example response returned from the backend:

{
  "result": [
    { "id": 1, "name": "John Doe", "email": "[email protected]" },
    { "id": 2, "name": "Jane Smith", "email": "[email protected]" }
  ],
  "count": 25
}

This structure is required because WebApiAdaptor expects the data array in result and the total record count in count to properly handle paging, searching, and filtering.

Backend payload reference

var payload = new
{
    result = filtered.Skip(Math.Max(skip, 0)).Take(Math.Max(top, 0)),
    count = filtered.Count
};
return Ok(payload);
import { MentionModule } from '@syncfusion/ej2-angular-dropdowns';
import { Component } from '@angular/core';
import { RichTextEditorModule, ToolbarService, HtmlEditorService, ImageService, LinkService, QuickToolbarService, TableService, PasteCleanupService } from '@syncfusion/ej2-angular-richtexteditor';
import { NgStyle } from '@angular/common';
import { DataManager, Query, WebApiAdaptor } from '@syncfusion/ej2-data';

@Component({
  imports: [RichTextEditorModule, MentionModule],
  standalone: true,
  selector: 'app-root',
  template: `
  <div>
    <ejs-richtexteditor
      id="mention_integration"
      placeholder="Type @ and tag the name"
    >
      <ng-template #valueTemplate>
        <p>
          Hello
          <span contenteditable="false" class="e-mention-chip"
            ><a href="mailto:[email protected]" title="[email protected]"
              >Maria</a
            ></span
          >&#8203;
        </p>
        <p>
        The suggestion list displays only 5 items when typing the &#64; character, as the data source contains a large set of entries
        </p>
      </ng-template>
    </ejs-richtexteditor>
  </div>
  <div>
   <ejs-mention [dataSource]='searchData' [query]='query' [fields]='fields' popupWidth='250px' target='#mention_integration_rte-edit-view'></ejs-mention>
  </div>
`,
  providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, QuickToolbarService, TableService, PasteCleanupService, NgStyle]
})
export class App {
  public searchData: DataManager = new DataManager({
    url: 'https://services.syncfusion.com/angular/production/api/Employees',
    adaptor: new WebApiAdaptor,
    crossDomain: true,
    offline: true // for local querying
  });
  public query: Query = new Query().select(['FirstName', 'EmployeeID']).take(7).requiresCount();
  public fields: Object = { text: 'FirstName', value: 'EmployeeID' };
}
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app.component';
import 'zone.js';

bootstrapApplication(App).catch((err) => console.error(err));
@import '../node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-richtexteditor/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind3.css';

Additional resources