A UI-platform-independent library for managing mention-like completion
JavaScript HTML
Latest commit aa0ff4c Oct 12, 2015 @rreusser Silly typo fix
Failed to load latest commit information.



js-standard-style Build Status npm version Dependency Status

A UI-platform-independent module for managing mention-like completion


This module implements an EventEmitter object which uses provided regexes to detect and replace @username- or #hashtag-like patterns in a (platform-independent) input field. The entity it interacts with must only have a value, a selection range, and presumably its own UI with which the user may select a completion.

It exists because I found myself working with React Native, implementing the gory details of mention detection and replacement. There's no shortage of HTML-based UI widgets that implement mention detection and completion, but all I needed was a platform-independent utility that would handle the dirty work, leaving me free to hook up the rest trivially.


$ npm install mention-completer


For a live HTML-based example, see here. Note that the code consists almost entirely of connecting the text field.

HTML Screenshot

var MentionCompleter = require('mention-completer')

var completer = new MentionCompleter({
  patterns: { handle: /(@[\w]+)\b/, hashtag: /(#[\w]+)\b/ },
  getSelectionRange: ...,
  setSelectionRange: ...,
  getValue: ...,
  setValue: ...

  .on('match', function(match) {
    // Trigger a remote API lookup and expose a UI element here, perhaps.
    // Returns, e.g.:
    //     match = { value:'@usern', type:'handle', range:{start: 7, end: 13} }
  .on('nomatch', function() {
    // Hook into this event to close the menu

// Trigger a check when an input is modified:
$('#my-input-field').on('keyup', completer.checkForMatch.bind(completer) )

// 1) User types "hello, @usern"
// 2) Your code exposes a UI completion selector and calls 'replaceMatch'
//    based on user input, e.g.:
//        completer.replaceMatch( match, '@username' )
// 3) If setValue and setSelectionRange are provided, they are triggered
//    automatically with the inserted match. Otherwise you may use the
//    'replace' event to trigger them manually.




Constructor for a new mention completer. Note that all get/set callbacks function in receive a callback with which they must return their result using the format callback(err, result). Option parameters are:

  • patterns: An associative array of pattern names and regexes. Recommended patterns are:
patterns: {
  handle: /(@[\w]+)\b/,
  hashtag: /(#[\w]+)\b/,
  • getValue: function( callback(err,value) ): A callback that queries the current text field value. It may execute the callback either synchronously or asynchronously with its resulting value.

  • setValue: function( value ) (optional): A callback that inserts the result after replacement. If provided, is triggered automatically on each replace event.

  • getSelectionRange: function( callback(err, {start, end}) ): A callback that queries the selection range and returns a range with format {start, end}.

  • setSelectionRange: function( {start, end} ) (optional): A callback that sets the selection range. If provided, is automatically called on each replace event.


  • .mostRecentMatch: the most recent match. If no match is present, equal to null.



Check for a completable match. Uses getSelectionRange and getValue to query the range and value. Emits a match event if a match is present and nomatch otherwise. You must hook this up automatically. A simple case is:

$('#input').on('change', completer.checkForMatch.bind(completer) )

.replaceMatch( {string, range: {start,end}}, text )

Replaces a match with text. The format of the first argument matches the value provided with the match event. Note that for consistency, if replaceMatch is triggered before a check resulting from new input completes, the value of the text field at the time of the check will be used. In reality, the delay to get the value and selection range is expected to be extremely short (synchronous or very nearly so) compared to the time between input events (100ms?). A simple use-case would be:

var currentMatch

mention.on('match', function(match) {
  currentMatch = match

// Result of user action:
mention.replaceMatch(currentMatch, '@suggestedname')

.on(eventName, callback(data))

Along with all other EventEmitter methods, on may be used to hook into any event. Note that these events are used internally so that .off may break functionality unless only a specific callback is detached.


  • check: Triggered just before the field is checked for a match. data contains {value, range:{start,end}} where value is the queried value of the input and range is the queried selection range.

  • match: Triggered when a completable match is detected. data contains {string, value, type, range:{start,end}} where string is the matched string, value is the match captured by the regex, type is the key of the pattern that was matched, and range contains the integer range of value within string.

  • nomatch: Triggered when no completable match is detected. Perhaps a good place to trigger closing of a suggestion menu.

  • replace: Triggered when replaceWith is explicitly called. data contains the computed replacement, {text, selectionRange:{start,end}} where text contains the text with replacement, and selectionRange contains the post-replacement selection range. If provided, setSelectionRange and setValue will update the input value.

  • error: Triggered when something goes wrong. data contains a string message.


This module uses Standard JS style. To test, run

$ npm run test


To bundle a browserified (output in dist/bundle.js), run

$ npm install -g browserify
$ npm run bundle


ISC License, (c) 2015 Ricky Reusser