Contribute to this guideReport an issue

Autocomplete Documentation

The optional Autocomplete plugin, introduced in CKEditor 4.10, provides contextual completion functionality for custom text matches based on user input. Every time the user types a pre-configured special character, such as @ or #, they get information about available autocomplete suggestions displayed in a dedicated dropdown.

This feature is a base for implementing specialized autocomplete features, such as mentions, tags, emoji or custom implementations.

The following sample implements a few smart placeholders for Customer Care Service email responses. Start typing the name of one of the placeholders visible on the right side of the editor. Type [[ and accept the selected placeholder from the dropdown using the Enter or Tab key.

Related Features

Get Sample Source Code

  • Autocomplete
                    <!doctype html>
    <html lang="en">
      <meta charset="utf-8">
      <meta name="robots" content="noindex, nofollow">
      <script src=""></script>
      <textarea cols="80" id="editor1" name="editor1" rows="10">&lt;p&gt;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using &lt;a href=&quot;;&gt;CKEditor&lt;/a&gt;.&lt;/p&gt;</textarea>
        var PLACEHOLDERS = [{
            id: 1,
            name: 'address',
            title: 'Address',
            description: 'Customer Support correspondence address.'
            id: 2,
            name: 'assignee',
            title: 'Assignee Name',
            description: 'Ticket assignee name.'
            id: 3,
            name: 'deadline',
            title: 'Deadline Time',
            description: 'Utmost time to which technician should handle the issue.'
            id: 4,
            name: 'department',
            title: 'Department Name',
            description: 'Department name responsible for servicing this ticket.'
            id: 5,
            name: 'caseid',
            title: 'Case ID',
            description: 'Unique case number used to distinguish tickets.'
            id: 6,
            name: 'casename',
            title: 'Case Name',
            description: 'Name of the ticket provided by the user.'
            id: 7,
            name: 'contact',
            title: 'Contact E-mail',
            description: 'Customer Support contact e-mail address.'
            id: 8,
            name: 'customer',
            title: 'Customer Name',
            description: 'Receipent of your response.'
            id: 9,
            name: 'hotline',
            title: 'Hotline Number',
            description: 'Customer Support Hotline number.'
            id: 10,
            name: 'technician',
            title: 'Technician Name',
            description: 'Technician which will handle this ticket.'
        CKEDITOR.addCss('span > .cke_placeholder { background-color: #ffeec2; }');
        CKEDITOR.replace('editor1', {
          plugins: 'autocomplete,textmatch,toolbar,wysiwygarea,basicstyles,link,undo,placeholder',
          toolbar: [{
              name: 'document',
              items: ['Undo', 'Redo']
              name: 'basicstyles',
              items: ['Bold', 'Italic']
              name: 'links',
              items: ['Link', 'Unlink']
          on: {
            instanceReady: function(evt) {
              var itemTemplate = '<li data-id="{id}">' +
                '<div><strong class="item-title">{title}</strong></div>' +
                '<div><i>{description}</i></div>' +
                outputTemplate = '[[{title}]]<span>&nbsp;</span>';
              var autocomplete = new CKEDITOR.plugins.autocomplete(evt.editor, {
                textTestCallback: textTestCallback,
                dataCallback: dataCallback,
                itemTemplate: itemTemplate,
                outputTemplate: outputTemplate
              // Override default getHtmlToInsert to enable rich content output.
              autocomplete.getHtmlToInsert = function(item) {
                return this.outputTemplate.output(item);
        function textTestCallback(range) {
          if (!range.collapsed) {
            return null;
          return CKEDITOR.plugins.textMatch.match(range, matchCallback);
        function matchCallback(text, offset) {
          var pattern = /\[{2}([A-z]|\])*$/,
            match = text.slice(0, offset)
          if (!match) {
            return null;
          return {
            start: match.index,
            end: offset
        function dataCallback(matchInfo, callback) {
          var data = PLACEHOLDERS.filter(function(item) {
            var itemName = '[[' + + ']]';
            return itemName.indexOf(matchInfo.query.toLowerCase()) == 0;