# Advanced select

> Tom Select turns a `<select>` into a searchable, keyboard-friendly dropdown, for long option lists, multi-value fields and options with an avatar or a flag.

Build searchable single and multi-value selects with the Tom Select plugin, including optgroups, validation states, and rich options with avatars or flags.

Source: https://docs.tabler.io/ui/plugins/advanced-select

---

## Overview

[Tom Select](https://tom-select.js.org/) attaches to a normal `<select class="form-select">` and replaces it with a searchable dropdown. The original `<select>` stays in the DOM and keeps its value, so it still works in a plain HTML form.

```html
<select class="form-select" id="select-overview" data-placeholder="Pick a fruit">
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
  <option value="cherry">Cherry</option>
  <option value="date">Date</option>
  <option value="elderberry">Elderberry</option>
</select>
```

## Installation

Install Tom Select with npm:

```shell
npm install tom-select
yarn add tom-select
pnpm install tom-select
bun install tom-select
```

Or include it from a CDN. You need both the script and its stylesheet:

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.0/dist/libs/tom-select/dist/css/tom-select.bootstrap5.min.css" />
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.0/dist/libs/tom-select/dist/js/tom-select.base.min.js"></script>
```

## Usage

### Basic select

Add `class="form-select"` to a `<select>`, give it an `id`, then attach Tom Select to that id.

```html
<select class="form-select" id="select-basic">
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
  <option value="cherry">Cherry</option>
</select>
```

```js
new TomSelect('#select-basic', {
  copyClassesToDropdown: false,
});
```

### Placeholder

Native `<select>` elements don't support the `placeholder` attribute, so add `data-placeholder` instead. Tom Select reads it and shows it as the empty-state text.

```html
<select class="form-select" id="select-placeholder" data-placeholder="Pick a fruit">
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
  <option value="cherry">Cherry</option>
</select>
```

### Multiple values

Add the `multiple` attribute to let users pick more than one option. Tom Select shows each pick as a removable tag.

```html
<select class="form-select" id="select-multiple" multiple="true" data-placeholder="Pick fruits">
  <option value="apple">Apple</option>
  <option value="banana">Banana</option>
  <option value="cherry">Cherry</option>
  <option value="date">Date</option>
  <option value="elderberry">Elderberry</option>
</select>
```

```js
new TomSelect('#select-multiple', {
  copyClassesToDropdown: false,
});
```

### Optgroups

Group related options with `<optgroup>`. Tom Select shows the group label in the dropdown and keeps it searchable.

```html
<select class="form-select" id="select-optgroup" data-placeholder="Pick a fruit">
  <optgroup label="Citrus">
    <option value="orange">Orange</option>
    <option value="lemon">Lemon</option>
    <option value="lime">Lime</option>
  </optgroup>
  <optgroup label="Berries">
    <option value="strawberry">Strawberry</option>
    <option value="blueberry">Blueberry</option>
    <option value="raspberry">Raspberry</option>
  </optgroup>
</select>
```

### Validation states

Add `.is-valid` or `.is-invalid` to the `<select>` to show a validation state. Tom Select carries the class over to its own wrapper, so the field still shows the usual green or red styling.

```html
<div class="mb-3">
  <label class="form-label">Valid select</label>
  <select class="form-select is-valid" id="select-valid" data-placeholder="Pick a fruit">
    <option value="apple" selected>Apple</option>
    <option value="banana">Banana</option>
  </select>
</div>
<div>
  <label class="form-label">Invalid select</label>
  <select class="form-select is-invalid" id="select-invalid" data-placeholder="Pick a fruit">
    <option value="apple">Apple</option>
    <option value="banana">Banana</option>
  </select>
</div>
```

### Rich options

An option can carry extra markup - an avatar, a flag, a badge - through a `data-custom-properties` attribute. Read it in a custom `render.option` / `render.item` function and show it next to the option text.

```html
<select class="form-select" id="select-avatar" data-placeholder="Assign to…">
  <option value="1" data-custom-properties="<span class=&quot;avatar avatar-xs&quot;>JD</span>">Jane Doe</option>
  <option value="2" data-custom-properties="<span class=&quot;avatar avatar-xs&quot;>MS</span>">Mark Smith</option>
  <option value="3" data-custom-properties="<span class=&quot;avatar avatar-xs&quot;>AK</span>">Amy Kim</option>
</select>
```

```html
<select class="form-select" id="select-flag" data-placeholder="Pick a country">
  <option value="us" data-custom-properties="<span class=&quot;flag flag-xs flag-country-us&quot;></span>">United States</option>
  <option value="gb" data-custom-properties="<span class=&quot;flag flag-xs flag-country-gb&quot;></span>">United Kingdom</option>
  <option value="de" data-custom-properties="<span class=&quot;flag flag-xs flag-country-de&quot;></span>">Germany</option>
  <option value="fr" data-custom-properties="<span class=&quot;flag flag-xs flag-country-fr&quot;></span>">France</option>
</select>
```

```js
function renderOption(data, escape) {
  if (data.customProperties) {
    return `<div class="dropdown-item"><span class="dropdown-item-indicator">${data.customProperties}</span>${escape(data.text)}</div>`;
  }
  return `<div>${escape(data.text)}</div>`;
}

new TomSelect('#select-avatar', {
  copyClassesToDropdown: false,
  dropdownParent: 'body',
  render: {
    item: renderOption,
    option: renderOption,
  },
});
```

Tom Select turns a `data-custom-properties` attribute into `data.customProperties` on the option object, so `render.option` and `render.item` can read it. `escape()` keeps user-supplied text safe when it is inserted as HTML.

### Common options

These are the options you will need most often. Tom Select has more, and they are listed in its [documentation](https://tom-select.js.org/docs/).

| Option | What it does |
| --- | --- |
| `copyClassesToDropdown` | Copies the `<select>` classes onto the dropdown. Tabler keeps this `false` and styles the dropdown itself. |
| `dropdownParent` | Where the dropdown is appended in the DOM, for example `'body'` so it is not clipped by a card or modal. |
| `maxItems` | Maximum number of selected items on a multi-value select. |
| `create` | `true` lets users type a value that is not in the option list. |
| `plugins` | List of Tom Select plugins to enable, for example `remove_button`. |
| `render.option` / `render.item` | Custom render functions for a dropdown option and a selected item. |

## Accessibility

- Always add a `<label>` linked to the `<select>` with `for` and `id`. Tom Select's generated markup does not replace the need for a label.
- The dropdown can be operated with the keyboard: type to search, arrow keys to move between options, <kbd>Enter</kbd> to pick one, and <kbd>Backspace</kbd> to remove the last tag on a multi-value select.
- When using `render.option`, keep enough text content in the rendered HTML - an avatar or flag alone does not tell a screen reader user which option it is.
- Escape user-supplied text in custom render functions with the `escape` argument, so option text can never break out of the generated HTML.

<script>{`
window.addEventListener('load', function () {
	if (typeof TomSelect === 'undefined') return;

	function renderOption(data, escape) {
		if (data.customProperties) {
			return '<div class="dropdown-item"><span class="dropdown-item-indicator">' + data.customProperties + '</span>' + escape(data.text) + '</div>';
		}
		return '<div>' + escape(data.text) + '</div>';
	}

	['select-overview', 'select-basic', 'select-placeholder', 'select-multiple', 'select-optgroup', 'select-valid', 'select-invalid'].forEach(function (id) {
		var el = document.getElementById(id);
		if (el) new TomSelect(el, { copyClassesToDropdown: false, dropdownParent: 'body' });
	});

	['select-avatar', 'select-flag'].forEach(function (id) {
		var el = document.getElementById(id);
		if (el) {
			new TomSelect(el, {
				copyClassesToDropdown: false,
				dropdownParent: 'body',
				render: { item: renderOption, option: renderOption },
			});
		}
	});
});
`}</script>
