Tag Manager (a jQuery plugin)

Type some tags in the input field, and separate them with enter, comma, or tab:

Turn a simple input field into a tags manager with a line of code:

HTML markup

<input type="text" name="tags" placeholder="Tags" class="tm-input"/>



Using multiple tag manager instances on the same page

Note you can have as many instances of tag manager on a single page as you want, with no conflicts; but please read below how to manage them when retrieving user input during <form> submit.

Using tag manager in conjunction with typeahead.js

Note before v3.0 tagmanager was using bootstrap-typeahead, but bootstrap deprecated typeahead from bootstrap v3.0; in this example we are using twitter-typeahead, which is different from bootstrap-typeahead. Both bootstrap-typeahead and twitter-typeahead are developed by the same developers, but they have a different API; and twitter-typeahead is much more feature rich.

    var countries = new Bloodhound({
      datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
      queryTokenizer: Bloodhound.tokenizers.whitespace,
      limit: 10,
      prefetch: {
        url: '/ajax/countries/json',
        filter: function (list) {
          return $.map(list, function (country) {
            return { name: country };


    var tagApi = jQuery(".tm-input.tm-input-typeahead").tagsManager({
      prefilled: ["Angola", "Laos", "Nepal"]
    jQuery(".tm-input.tm-input-typeahead").typeahead(null, {
      name: 'countries',
      displayKey: 'name',
      source: countries.ttAdapter()
    }).on('typeahead:selected', function (e, d) {
        tagApi.tagsManager("pushTag", d.name);

Typeahead via Ajax source

Now that we are using typeahead.js from tag manger v3.0 it make sense to use typeahead.js own API to load typeahead from an ajax source, it's very flexible, it's very powerful, and we strongly suggest to read the docs on github to learn how to configure it, to give you a hint, look above at example explaining how to set up tag mangager in conjunction with typeahead.js and check the typeahead.js parameter prefetch.

Typeahead via function() source

Again, now that we are using typeahead.js from tag manger v3.0 you can use the powerful typeahead.js API to load from any source. Check the docs on github to learn how to configure a local source as a function(), look in the docs for typeahead.js parameter local.

Retrieving tags on <form> submit

By default, Tag Manager creates a hidden input field with a random generated name and stores its data in this hidden field as a comma separated list.

When processing the user input on form submit, simply parse the data of this hidden field.

Look for this hidden field in the examples above using a browser developer tool (such as Firefox Firebug). In the example you should see: <input type="hidden" value="Pisa,Rome" name="hidden-somerandomgeneratedcode">

It is also possible to give this field a customer name using the hiddenTagListName parameter, or create your own hidden field and use the hiddenTagListId parameter.

Note if you are using javascript to post the list of tags you may be much more interested in tag manage API, see below how to retrieve the list of tags entered by the user using the API or how to have tag manager automatically push the tags to a ajax location.

Pushing Tags via Ajax

Tagmanager can push entered tag values on-the-fly via Ajax as the user creates/deletes tags to a user-specified destination. Use the AjaxPushAllTags option to push all tag values on every update, rather than incrementally per created tag. Use the AjaxPushParameters option to add additional parameters on each ajax request. Refer to documentation for additional parameters.

        AjaxPush: '/ajax/countries/push',
        AjaxPushAllTags: true,
        AjaxPushParameters: { 'authToken': 'foobar' }

Note to see this working, open firebug or chrome developer tools and chech the XHR ajax request being generated when you add or remove tags from the input field above.

Using API

Tagmanager has a nice API you can use to programmatically add or remove tags, see below an example, and check the complete list of methods in the configuration section.

        AjaxPush: '/ajax/countries/push',
        AjaxPushAllTags: true,
        AjaxPushParameters: { 'authToken': 'foobar' }

Note to see this working, open firebug or chrome developer tools and chech the XHR ajax request being generated when you add or remove tags from the input field above.

Tagmanager Configuration

The code below show all the configurable options available (as of version 3.0.1):

        prefilled: ["Pisa", "Rome"],
        CapitalizeFirstLetter: true,
        AjaxPush: null,
        AjaxPushAllTags: null,
        AjaxPushParameters: null,
        delimiters: [9, 13, 44],
        backspace: [8]
        blinkBGColor_1: '#FFFF9C',
        blinkBGColor_2: '#CDE69C',
        hiddenTagListName: 'hiddenTagListA',
        hiddenTagListId: null,
        deleteTagsOnBackspace: true,
        tagsContainer: null,
        tagCloseIcon: '×',
        tagClass: '',
        validator: null,
        onlyTagList: false
prefilled Populates the initial tag values. Default: null. Allowed formats:
  • an Array of strings
  • a String delimited by the first char code in the delimiters parameter (comma by default)
  • a Function which returns an array
  • if prefill is not provided hiddenTagListId will be used as a fallback if provided. Note the value in hiddenTagListId should be a string delimited by the first char code in the delimiters parameter (comma by default)
CapitalizeFirstLetter If true, all tags will be displayed with first letter capitalized. Default: false.
AjaxPush Well, since we pull from an ajax source we can also push, don't we? So provide a url as AjaxPush and the added tag will be POSTed.
AjaxPushAllTags If true, enables a mode to sync the entire tag state via AJAX (rather than incrementally) each time a tag is added/deleted. Default: false.
AjaxPushParameters Adds an additional parameter payload to push with each AJAX request, for example server authentication parameters. Default: null.
delimiters Default: [9,13,44] (tab, enter, comma). Delimiters should be numeric ASCII char codes. Please note the following:
  • The following values are handled as key codes: 9 (tab), 13 (enter), 16 (shift), 17 (ctrl), 18 (alt), 19 (pause/break), 37 (leftarrow), 38 (uparrow), 39 (rightarrow), 40 (downarrow)
  • Note that codes 45 (key = insert, char = -) and 46 (key = delete, char = .) are handled as chars, so currently insert and delete keys cannot be used as delimiters
  • The first char code (non-key code) specified in the array will be used as the base delimiter for parsing tags to/from the hidden field "list string". This will default to comma if not specified.
  • See http://unixpapa.com/js/key.html for a full explanation of ASCII versus key codes.
backspace When the input field is empty, and some tags are rendered on the left of the input field, and the user hit the backspace the plugin remove the rightest tag (which is the last the user entered).
With this option you can provide an array of char codes (like the delimiters above) you want the system to use in place of the backspace (char code 8), or provide an empty array if you don't want this feature at all.
When a duplicate tag is entered the user is notified with a blinking of the corresponding (duplicate) tag, here you can configure the colors.

Note this is working only if you also include jQuery UI in your project.

output Should be a valid CSS selector, if present this input field will receive the comma separated list of tags entered; tag manager does not check if this selector is really an input field, just tries to fill the value with jQuery .val().
replace (true|false) If set, the element name of the input field will be transfered to the hidden input field storing the comma separated list of tags entered; in other words if you have <input name="tags"/> and you turn it into a tagmanager with this parameter true on form submit you will find the tags posted with name="tag".
maxTags Optionally defines the maximum number of tags accepted. Default: 0 (no limit)
tagCloseIcon Sets the HTML string to be used as the tag close icon. Default: ×
tagsContainer Optional jQuery selector for the element to contain the tags. Default: tags appear immediately before the tag input.
tagClass Optional class to be applied to all tags. Please as see note about automatic tag styling. Default: none
validator An optional callback function to validate the user input. Takes the tag string as input, and must return true or false. Default: null (no validation function used).
onlyTagList If true, rejects tags which do not appear in the typeahead list. Default: false

Tag Manager API Methods

You can pop the last added tag (the rightmost being show)


You can push a new tag


You can remove all tags


You can retrieve the list of all tags entered so far


Tag Manager Events

You can hook on events to get notified when things happen.

tm:afterPush when a new tag is pushed.

    jQuery(".tm-input").on('tm:afterPush', function(e, tag) {
      alert(tag + " was pushed!");

tm:beforePush when a new tag is going to be pushed.

    jQuery(".tm-input").on('tm:beforePush', function(e, tag) {
      alert(tag + " is almost there!");

tm:afterSplice when a new tag is removed.

    jQuery(".tm-input").on('tm:afterSplice', function(e, tag) {
      alert(tag + " was removed!");

tm:beforeSplice when a new tag is going to be removed.

    jQuery(".tm-input").on('tm:beforeSplice', function(e, tag) {
      alert(tag + " is almost removed!");

tm:afterPop when the last tag (rightmost) is removed.

    jQuery(".tm-input").on('tm:afterPop', function(e, tag) {
      alert(tag + " was removed!");

tm:beforePop when the last (rightmost) tag is going to be removed.

    jQuery(".tm-input").on('tm:beforePop', function(e, tag) {
      alert(tag + " is almost removed!");

tm:refresh when the hidden field containing the comma separated list of tags has been updated.

    jQuery(".tm-input").on('tm:refresh', function(e, taglist) {
      alert(taglist + " is the new list of tags!");

tm:emptied when all tags have been removed programmatically.

    jQuery(".tm-input").on('tm:emptied', function(e, taglist) {
      alert("Tags have been nuked!");

tm:hide when the input field has been hidden due to maxTags limit being hit.

    jQuery(".tm-input").on('tm:hide', function(e, taglist) {
      alert("Holy smoke how many tags!");

tm:show when the input field has been show again after being previously removed due to maxTags limit being hit.

    jQuery(".tm-input").on('tm:show', function(e, taglist) {
      alert("Here we are again!");

Tag Manager Styles

Tag Manager comes bundled with a Bootstrap-themed set semantic colors/sizes, and provides multiple methods to define tag styles:

  1. the CSS base class tm-tag is applied to all tags
  2. classes assigned to the input are used to infer the semantic tag classes, for example <input class='tm-input tm-input-success tm-input-small'/> will apply classes tm-tag-success and tm-tag-small to the tags.
  3. you can apply a custom class(es) to the tags using the tagClass parameter

In order to have correct alignment we recommend that you give the input the class tm-tag. If using TagManager within a Bootstrap control-group container, please also add tm-group class to the container node.


I built Tagmanager while working on an online personal finance tool, I wanted a simple solution to manage tags for each expense users were entering, like in stackoverflow and many other sites.

Alternatives to my Tagmanager

Before to develop my own tags manager I spent quite a lot of time investigating what other solutions where available, so I think I can spare you some time listing them here and explaining why I developed my own.

The four above are the best around in my opinion, but... I developed Tagmanager. I wanted it to be very simple, I didn't need a pencil mouse pointer when the user hover it. I didn't need inplace edit of the tag, I think it's confusing for the user. I didn't want the tag manager to generate a lot of html in the form.

And I wanted it to be fully integrated with bootstrap and typeahead.

Version 3.0 is in beta.

Version 3.0 is in beta, there are a couple of things which doesn't work as they should when used in conjunction with typeahes.js, please check discussion on github, we are trying to fix this.

Third-Party Add-ons

Further reading

If you have choosen to go this way and implement a tags system in one of your application you have probably already thought about a couple of things, like the database structure required to store tags.

But just in case... You can find some thoughts about that here.

Check the Tags Manager repository and download the scripts from github

Download from GitHub

Comments are welcome!