errohitsinghal/yii2-tom-select

Tom Select widget for Yii2 Framework with Bootstrap 5 support

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:yii2-extension

pkg:composer/errohitsinghal/yii2-tom-select

1.0 2026-01-02 18:08 UTC

This package is auto-updated.

Last update: 2026-01-02 19:01:38 UTC


README

Latest Stable Version Total Downloads License Buy Me A Coffee

A Yii2 widget for Tom Select - a dynamic, framework agnostic, and lightweight (~16kb gzipped) <select> UI control with autocomplete and native-feeling keyboard navigation.

Features

  • ✅ Bootstrap 5 integration
  • ✅ Local vendor files (no CDN dependencies)
  • ✅ Full Tom Select API support
  • ✅ All plugins included
  • ✅ Model and non-model usage
  • ✅ AJAX/Remote data loading
  • ✅ Custom rendering templates
  • ✅ Event handling
  • ✅ Multi-select support
  • ✅ Tagging/item creation
  • ✅ Diacritics support

Requirements

  • PHP >= 7.4
  • Yii2 >= 2.0.45
  • yii2-bootstrap5 >= 2.0.0

Installation

Install via Composer:

composer require errohitsinghal/yii2-tom-select

Quick Start

Basic Select

use errohitsinghal\tomselect\TomSelect;

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'category_id',
    'items' => [
        '1' => 'Option 1',
        '2' => 'Option 2',
        '3' => 'Option 3',
    ],
]) ?>

Without Model

<?= TomSelect::widget([
    'name' => 'category',
    'value' => 1,
    'items' => [
        '1' => 'Option 1',
        '2' => 'Option 2',
        '3' => 'Option 3',
    ],
]) ?>

Multi-Select

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'tags',
    'items' => $tagList,
    'multiple' => true,
    'plugins' => ['remove_button', 'clear_button'],
]) ?>

Configuration Options

Widget Properties

Property Type Default Description
type string 'select' Input type: 'select' or 'text'
items array [] Option items for select element
multiple bool false Allow multiple selections
placeholder string null Placeholder text
create bool/callable false Allow creating new items
maxItems int/null 1 Maximum items (null for unlimited)
maxOptions int/null 50 Maximum options in dropdown
plugins array [] Tom Select plugins to enable
clientOptions array [] Tom Select configuration options
clientEvents array [] Event handlers
useBaseAsset bool false Use base asset (without plugins)
hideSelected bool null Hide selected items from dropdown
closeAfterSelect bool null Close dropdown after selection
allowEmptyOption bool false Allow empty option
searchField string/array null Fields to search in
sortField string/array null Fields to sort by
valueField string 'value' Property to use as value
labelField string 'text' Property to use as label
diacritics bool true Enable diacritics support
loadThrottle int 300 Load throttle in milliseconds
preload bool/string false Preload options (true, false, or 'focus')
renderOption JsExpression null Custom option render function
renderItem JsExpression null Custom item render function
load JsExpression null Remote data loading function
variableName string null JS variable name for instance

Available Plugins

Tom Select comes with many built-in plugins:

Plugin Description
caret_position Use ← and → arrow keys to move between items
change_listener Update when underlying input changes
checkbox_options Display checkboxes in dropdown
clear_button Add button to clear all selections
drag_drop Allow drag-n-drop sorting of items
dropdown_header Add header to dropdown
dropdown_input Move input to dropdown
input_autogrow Auto-grow input width as user types
no_active_items Disable selecting items
no_backspace_delete Prevent backspace deletion
optgroup_columns Display optgroups as columns
remove_button Add remove button to each item
restore_on_backspace Restore deleted items on backspace
virtual_scroll Virtual scrolling for large lists

Usage with ActiveForm

The widget integrates seamlessly with Yii2's ActiveForm. You have two options:

Option 1: Using the Widget Directly

use errohitsinghal\tomselect\TomSelect;
use yii\bootstrap5\ActiveForm;

<?php $form = ActiveForm::begin(); ?>

<?= $form->field($model, 'category_id')->widget(TomSelect::class, [
    'items' => $categories,
    'placeholder' => 'Select a category...',
]) ?>

<?= $form->field($model, 'tags')->widget(TomSelect::class, [
    'items' => $tagList,
    'multiple' => true,
    'create' => true,
    'plugins' => ['remove_button'],
]) ?>

<?php ActiveForm::end(); ?>

Option 2: Using the ActiveField Extension

For a cleaner syntax, use the custom ActiveField class:

use errohitsinghal\tomselect\TomSelect;
use errohitsinghal\tomselect\ActiveField;
use yii\bootstrap5\ActiveForm;

<?php $form = ActiveForm::begin([
    'fieldClass' => ActiveField::class,
]); ?>

// Basic select
<?= $form->field($model, 'category_id')->tomSelect($categories, [
    'placeholder' => 'Select a category...',
]) ?>

// Multi-select with plugins
<?= $form->field($model, 'tags')->tomSelect($tagList, [
    'multiple' => true,
    'plugins' => ['remove_button', 'clear_button'],
]) ?>

// Tagging field (pre-configured)
<?= $form->field($model, 'keywords')->tomSelectTags() ?>

// AJAX loading field
<?= $form->field($model, 'user_id')->tomSelectAjax('/api/users/search', [
    'valueField' => 'id',
    'labelField' => 'name',
    'searchField' => ['name', 'email'],
    'placeholder' => 'Search users...',
]) ?>

<?php ActiveForm::end(); ?>

ActiveField Helper Methods

Method Description
tomSelect($items, $options) Basic Tom Select field
tomSelectTags($options) Pre-configured tagging field with remove buttons
tomSelectAjax($url, $options) Pre-configured AJAX loading field

Complete Form Example

<?php

use errohitsinghal\tomselect\ActiveField;
use yii\bootstrap5\ActiveForm;
use yii\web\JsExpression;

?>

<?php $form = ActiveForm::begin([
    'fieldClass' => ActiveField::class,
]); ?>

<div class="row">
    <div class="col-md-6">
        <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
    </div>
    <div class="col-md-6">
        <?= $form->field($model, 'status')->tomSelect([
            'draft' => 'Draft',
            'published' => 'Published',
            'archived' => 'Archived',
        ]) ?>
    </div>
</div>

<div class="row">
    <div class="col-md-6">
        <?= $form->field($model, 'category_id')->tomSelect($categories, [
            'placeholder' => 'Select category...',
            'allowEmptyOption' => true,
        ]) ?>
    </div>
    <div class="col-md-6">
        <?= $form->field($model, 'tags')->tomSelectTags([
            'placeholder' => 'Add tags...',
        ]) ?>
    </div>
</div>

<div class="row">
    <div class="col-md-6">
        <?= $form->field($model, 'author_id')->tomSelectAjax('/api/users/search', [
            'placeholder' => 'Search author...',
        ]) ?>
    </div>
    <div class="col-md-6">
        <?= $form->field($model, 'related_posts')->tomSelect($posts, [
            'multiple' => true,
            'maxItems' => 5,
            'plugins' => ['remove_button'],
            'placeholder' => 'Select up to 5 related posts...',
        ]) ?>
    </div>
</div>

<div class="form-group">
    <?= \yii\bootstrap5\Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
</div>

<?php ActiveForm::end(); ?>

Form Validation Styling

Tom Select automatically integrates with Bootstrap 5 validation states. When a field has validation errors, the Tom Select control will display with the appropriate error styling.

// The widget respects Yii2's validation
<?= $form->field($model, 'category_id')->tomSelect($categories) ?>

// Custom error display
<?= $form->field($model, 'category_id', [
    'errorOptions' => ['class' => 'invalid-feedback d-block'],
])->tomSelect($categories) ?>

Usage Examples

With Option Groups

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'vehicle',
    'items' => [
        'Cars' => [
            'toyota' => 'Toyota',
            'honda' => 'Honda',
            'ford' => 'Ford',
        ],
        'Motorcycles' => [
            'yamaha' => 'Yamaha',
            'kawasaki' => 'Kawasaki',
        ],
    ],
]) ?>

Tagging (Create New Items)

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'tags',
    'type' => TomSelect::TYPE_TEXT,
    'multiple' => true,
    'create' => true,
    'plugins' => ['remove_button'],
    'clientOptions' => [
        'persist' => false,
        'createOnBlur' => true,
    ],
]) ?>

With Plugins and Options

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'users',
    'items' => $userList,
    'multiple' => true,
    'plugins' => [
        'remove_button' => [
            'title' => 'Remove this item',
        ],
        'clear_button' => [
            'title' => 'Remove all items',
        ],
        'dropdown_header' => [
            'title' => 'Select Users',
        ],
    ],
]) ?>

AJAX Remote Data Loading

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'user_id',
    'placeholder' => 'Search for a user...',
    'clientOptions' => [
        'valueField' => 'id',
        'labelField' => 'name',
        'searchField' => ['name', 'email'],
        'load' => new \yii\web\JsExpression('function(query, callback) {
            if (!query.length) return callback();
            fetch("/api/users/search?q=" + encodeURIComponent(query))
                .then(response => response.json())
                .then(json => callback(json.results))
                .catch(() => callback());
        }'),
    ],
]) ?>

With Custom Rendering

use yii\web\JsExpression;

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'country_id',
    'items' => $countries,
    'renderOption' => new JsExpression('function(data, escape) {
        return "<div class=\"option\">" +
            "<img src=\"/img/flags/" + escape(data.value) + ".png\" class=\"me-2\" width=\"20\">" +
            "<span>" + escape(data.text) + "</span>" +
        "</div>";
    }'),
    'renderItem' => new JsExpression('function(data, escape) {
        return "<div class=\"item\">" +
            "<img src=\"/img/flags/" + escape(data.value) + ".png\" class=\"me-2\" width=\"16\">" +
            escape(data.text) +
        "</div>";
    }'),
]) ?>

Event Handling

use yii\web\JsExpression;

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'category',
    'items' => $categories,
    'clientEvents' => [
        'change' => new JsExpression('function(value) {
            console.log("Selected:", value);
            // Trigger dependent dropdown update
            updateSubcategories(value);
        }'),
        'item_add' => new JsExpression('function(value, item) {
            console.log("Added item:", value);
        }'),
        'dropdown_open' => new JsExpression('function(dropdown) {
            console.log("Dropdown opened");
        }'),
    ],
]) ?>

Virtual Scroll for Large Datasets

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'product_id',
    'placeholder' => 'Search products...',
    'plugins' => ['virtual_scroll'],
    'maxOptions' => null,
    'clientOptions' => [
        'valueField' => 'id',
        'labelField' => 'name',
        'searchField' => 'name',
        'firstUrl' => new JsExpression('function(query) {
            return "/api/products?q=" + encodeURIComponent(query) + "&page=1";
        }'),
        'load' => new JsExpression('function(query, callback) {
            const url = this.getUrl(query);
            fetch(url)
                .then(response => response.json())
                .then(json => {
                    this.setNextUrl(query, json.next_url);
                    callback(json.results);
                })
                .catch(() => callback());
        }'),
    ],
]) ?>

Using HTML Options

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'status',
    'items' => $statuses,
    'options' => [
        'id' => 'custom-id',
        'class' => 'custom-class',
        'data-custom' => 'value',
    ],
]) ?>

Dependent/Chained Dropdowns

// Parent dropdown
<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'country_id',
    'items' => $countries,
    'variableName' => 'countrySelect',
    'clientEvents' => [
        'change' => new JsExpression('function(value) {
            citySelect.clear();
            citySelect.clearOptions();
            if (value) {
                citySelect.load(value);
            }
        }'),
    ],
]) ?>

// Child dropdown
<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'city_id',
    'items' => [],
    'variableName' => 'citySelect',
    'placeholder' => 'Select country first...',
    'clientOptions' => [
        'valueField' => 'id',
        'labelField' => 'name',
        'load' => new JsExpression('function(countryId, callback) {
            fetch("/api/cities?country_id=" + countryId)
                .then(response => response.json())
                .then(json => callback(json))
                .catch(() => callback());
        }'),
    ],
]) ?>

Pre-populating with Initial Options (AJAX)

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'user_id',
    'clientOptions' => [
        'valueField' => 'id',
        'labelField' => 'name',
        'searchField' => ['name', 'email'],
        // Pre-populate with selected option
        'options' => [
            [
                'id' => $model->user_id,
                'name' => $model->user->name,
                'email' => $model->user->email,
            ],
        ],
        'items' => [$model->user_id],
        'load' => new JsExpression('function(query, callback) {
            if (!query.length) return callback();
            fetch("/api/users?q=" + encodeURIComponent(query))
                .then(response => response.json())
                .then(json => callback(json))
                .catch(() => callback());
        }'),
    ],
]) ?>

API Methods

After initialization, you can access the Tom Select instance through the variable name:

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'category',
    'items' => $categories,
    'variableName' => 'mySelect',
]) ?>

<script>
// Clear selection
mySelect.clear();

// Add option dynamically
mySelect.addOption({value: 'new', text: 'New Option'});

// Select an item
mySelect.addItem('new');

// Remove option
mySelect.removeOption('value');

// Get current value
var value = mySelect.getValue();

// Set value
mySelect.setValue('newValue');

// Lock/disable
mySelect.lock();
mySelect.disable();

// Unlock/enable
mySelect.unlock();
mySelect.enable();

// Destroy instance
mySelect.destroy();
</script>

Using Base Asset (Smaller Footprint)

If you don't need all plugins, use the base asset:

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'category',
    'items' => $categories,
    'useBaseAsset' => true,
]) ?>

Client Options Reference

All Tom Select options are supported via the clientOptions property. Common options include:

Option Type Default Description
options array [] Initial options list
items array [] Initial selected values
create bool/function false Allow creating new items
createOnBlur bool false Create item when leaving field
createFilter RegExp/string/function null Filter for new items
delimiter string ',' Delimiter for multiple values
highlight bool true Highlight matches
persist bool true Persist created items
openOnFocus bool true Open dropdown on focus
maxOptions int 50 Max options to display
maxItems int null Max items to select
hideSelected bool null Hide selected from dropdown
closeAfterSelect bool undefined Close after selection
allowEmptyOption bool false Allow empty option
loadThrottle int 300 Throttle for load requests
loadingClass string 'loading' Loading state CSS class
placeholder string undefined Placeholder text
preload bool/string false Preload options
dropdownParent string null Dropdown container
addPrecedence bool false "Add..." is default selection
selectOnTab bool false Select with tab key
diacritics bool true International character support
duplicates bool false Allow duplicate selections

Events Reference

Event Arguments Description
initialize - Control initialized
change value Value changed
focus - Control focused
blur - Control blurred
item_add value, item Item added
item_remove value, item Item removed
item_select item Item selected
clear - Selection cleared
option_add value, data Option added
option_remove value Option removed
option_clear - All options cleared
optgroup_add id, data Option group added
optgroup_remove id Option group removed
optgroup_clear - All option groups cleared
dropdown_open dropdown Dropdown opened
dropdown_close dropdown Dropdown closed
type str User typing
load data Options loaded
destroy - Control destroyed

Styling

The widget uses Bootstrap 5 themed CSS by default. You can customize the appearance:

Override CSS Variables

.ts-wrapper {
    --ts-pr-clear-button: 0;
    --ts-pr-caret: 0;
    --ts-pr-min: .75rem;
}

.ts-control {
    border-radius: 0.375rem;
}

.ts-dropdown {
    border-radius: 0.375rem;
}

Custom CSS Classes

<?= TomSelect::widget([
    'model' => $model,
    'attribute' => 'category',
    'items' => $categories,
    'options' => [
        'class' => 'my-custom-select',
    ],
]) ?>

Troubleshooting

Widget not initializing

Make sure jQuery is loaded if you're using it elsewhere. Tom Select doesn't require jQuery but may conflict if jQuery plugins modify the DOM.

Options not showing

Check that your items array is properly formatted:

'items' => [
    'value1' => 'Label 1',
    'value2' => 'Label 2',
]

AJAX not working

Ensure your endpoint returns JSON in the expected format:

[
    {"id": 1, "name": "Option 1"},
    {"id": 2, "name": "Option 2"}
]

And configure valueField and labelField to match your data structure.

Multiple selection not working

Make sure multiple is set to true:

'multiple' => true,

Support

If you find this package helpful, consider buying me a coffee! ☕

Buy Me A Coffee

License

This package is open-sourced software licensed under the MIT License.

Credits

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Changelog

1.0.0

  • Initial release
  • Full Tom Select API support
  • Bootstrap 5 integration
  • All plugins included
  • Comprehensive documentation