---
category: features
---
{@snippet features/build-mention-source}
# Mention
The {@link module:mention/mention~Mention} feature brings support for smart completion based on user input. When user types a pre-configured marker, such as `@` or `#`, they get an autocomplete suggestions in a balloon panel displayed next to the caret. The selected suggestion is then inserted into the content.
## Demo
You can type `'@'` character to invoke mention auto-complete UI. The below demo is configured as static list of names.
{@snippet features/mention}
## Installation
This feature is enabled by default in all builds. The installation instructions are for developers interested in building their own, custom editor.
To add this feature to your editor, install the [`@ckeditor/ckeditor5-mention`](https://www.npmjs.com/package/@ckeditor/ckeditor5-mention) package:
```bash
npm install --save @ckeditor/ckeditor5-mention
```
Then add `Mention` to your plugin list and {@link module:mention/mention~MentionConfig configure} the feature (if needed):
```js
import Mention from '@ckeditor/ckeditor5-mention/src/mention';
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ Mention, ... ],
mention: {
// configuration...
}
} )
.then( ... )
.catch( ... );
```
## Configuration
The minimal configuration of a mention requires defining a feed and optionally a marker (if not using the default `@` character).
### Providing the feed
The {@link module:mention/mention~MentionFeed#feed} can be provided as:
- static array - good for scenarios with relatively small set of auto-complete items.
- a callback - which provides more control over the returned list of items.
If using a callback you can return a `Promise` that resolves with list of {@link module:mention/mention~MentionFeedItem mention feed items}. Those can be simple stings used as mention text or plain objects with at least one `name` property. The other parameters can be used either when {@link #customizing-the-auto-complete-list customizing the auto-complete list} {@link #customizing-the-output customizing the output}.
The callback receives a matched text which should be used to filter item suggestions. The callback should return a Promise and resolve it with an array of items that match to the feed text.
The mention feature does not limit items displayed in the mention suggestion list when using the callback. You should limit the output by yourself.
```js
const items = [
{ id: '1', name: 'Barney Stinson', username: 'swarley' },
{ id: '2', name: 'Lily Aldrin', username: 'lilypad' },
{ id: '3', name: 'Marshall Eriksen', username: 'marshmallow' },
{ id: '4', name: 'Robin Scherbatsky', username: 'rsparkles' },
{ id: '5', name: 'Ted Mosby', username: 'tdog' }
];
function getFeedItems( feedText ) {
// As an example of asynchronous action return a promise that resolves after a 100ms timeout.
return new Promise( resolve => {
setTimeout( () => {
resolve( items.filter( isItemMathing ) );
}, 100 );
} );
// Filtering function - it uses `name` and `username` properties of an item to find a match.
function isItemMatching( item ) {
// Make search case-insensitive.
const searchString = feedText.toLowerCase();
// Include an item in the search results if name or username includes the current user input.
return textIncludesSearchSting( item.name, searchString ) || textIncludesSearchSting( item.username, searchString );
}
function textIncludesSearchSting( text, searchString ) {
return text.toLowerCase().includes( searchString );
}
}
```
### Customizing the auto-complete list
The list displayed in auto-complete list can be customized by defining the {@link module:mention/mention~MentionFeed#itemRenderer} callback.
This callback takes a plain object feed item (at least with `name` parameter - even when feed items are defined as strings). It should return a new DOM element.
```js
function customItemRenderer( item ) {
const span = document.createElement( 'span' );
span.classList.add( 'custom-item' );
span.id = `mention-list-item-id-${ item.id }`;
span.innerHTML = `${ item.name } @${ item.username }`;
return span;
}
```
### Customizing the output
In order to have full control over the markup generated by the editor you can overwrite the conversion process. To do that you must specify both {@link module:engine/conversion/upcastdisatcher upcast} and {@link module:engine/conversion/downcastdisatcher downcast} converters.
```js
import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
// The link plugin using highest priority in conversion pipeline.
const HIGHER_THEN_HIGHEST = priorities.highest + 50;
// The upcast converter will convert elements to the model 'mention' attribute.
editor.conversion.for( 'upcast' ).elementToAttribute( {
view: {
name: 'a',
key: 'data-mention',
classes: 'mention',
attributes: {
href: true
}
},
model: {
key: 'mention',
value: viewItem => {
// Optionally: do not convert partial mentions.
if( !isFullMention(viewItem)){
return;
}
// The mention feature expects that mention attribute value in the model is a plain object:
const mentionValue = {
// The name attribute is required by mention editing.
name: viewItem.getAttribute( 'data-mention' ),
// Add any other properties as required.
link: viewItem.getAttribute( 'href' ),
id: viewItem.getAttribute( 'data-user-id' )
};
return mentionValue;
}
},
converterPriority: HIGHER_THEN_HIGHEST
} );
function isFullMention( viewElement ) {
const textNode = viewElement.getChild( 0 );
// Do not parse empty mentions.
if ( !textNode || !textNode.is( 'text' ) ) {
return;
}
const mentionString = textNode.data;
// Assume that mention is set as marker + mention name.
const marker = mentionString.slice( 0, 1 );
const name = mentionString.slice( 1 );
// Do not upcast partial mentions - might come from copy-paste of partially selected mention.
if ( name != dataMention ) {
return;
}
}
// Don't forget to define a downcast converter as well:
editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'mention',
view: ( modelAttributeValue, viewWriter ) => {
if ( !modelAttributeValue ) {
// Do not convert empty attributes.
return;
}
return viewWriter.createAttributeElement( 'a', {
class: 'mention',
'data-mention': modelAttributeValue.name,
'data-user-id': modelAttributeValue.id,
'href': modelAttributeValue.link
} );
},
converterPriority: HIGHER_THEN_HIGHEST
} );
```
## Common API
The {@link module:mention/mention~Mention} plugin registers:
* the `'mention'` command implemented by {@link module:mention/mentioncommand~MentionCommand}.
You can insert a mention element by executing the following code:
```js
editor.execute( 'mention', { marker: '@', mention: 'John' } );
```
## Contribute
The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-mention.