Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/vueform/multiselect

Vue 3 multiselect component with single select, multiselect and tagging options (+Tailwind CSS support).
https://github.com/vueform/multiselect

autocomplete dropdown multiselect multiselectdropdown select select-multiple select2 selectbox tailwind tailwindcss vue vue-select vue2 vue3 vuejs vueselect

Last synced: about 1 month ago
JSON representation

Vue 3 multiselect component with single select, multiselect and tagging options (+Tailwind CSS support).

Awesome Lists containing this project

README

        



npm

CircleCI




npm bundle size (scoped version)


Discord


npm

Vue 3 Multiselect
















## Sponsors







Vueform




Vueform is comprehensive **form development framework** for Vue.js. It supercharges and standardizes the entire form building process and takes care of everything from rendering to validation and processing. With our latest tool, the **Drag and Drop Form Builder**, you can allow your developers & non-tech workforce to build the most complex forms without coding.

Feature highlights:
- integrate Vueform **Drag and Drop Form Builder** into **any application**
- save forms in **database** as a JSON
- use your **own form elements** with **custom configuration** options
- a complete theming and templating system with **Tailwind support**
- 25+ form elements with **multi-file uploads**, date pickers and rich text editor
- element **nesting** and **repeating**
- **50+ validators** with async, dependent and custom rules
- **conditional logic** on element & form level
- breaking forms into **steps** with **form wizard**
- **translating** form content and global i18n support.


Vueform Builder




**Learn more:**
- Builder: [https://builder.vueform.com](https://builder.vueform.com?cid=multiselect)
- Framework: [https://vueform.com](https://vueform.com?cid=multiselect)

**Other Vueform libraries:**

* [@vueform/slider](https://github.com/vueform/slider) - Vue 3 slider component with multihandles, tooltips merging and formatting.
* [@vueform/toggle](https://github.com/vueform/toggle) - Vue 3 toggle component with labels, custom slots and styling options.

## Comparison with other libraries

| Feature | @vueform/multiselect | vue-multiselect | vue-select |
| --- | :-: | :-: | :-: |
| Basic Features* |
| Vue.js 2 support | ✓ | ✓ | ✓ |
| Vue.js 3 support | ✓ | ~ | ~ |
| Single select | ✓ | ✓ | ✓ |
| Multiselect (without tags) | ✓ | ✓ | - |
| Tags | ✓ | ✓ | ✓ |
| Search & filtering | ✓ | ✓ | ✓ |
| Option groups | ✓ | ✓ | - |
| Advanced features* |
| Async search | ✓ | ~ | ~ |
| New option when using tags | ✓ | ~ | ✓ |
| New option when not using tags | ✓ | - | ✓ |
| New option validation | ✓ | ~ | - |
| Infinite scroll | ✓ | ~ | ~ |
| Append to body | ✓ | ~ | ✓ |
| Object value support | ✓ | ✓ | - |
| Accents/diacritics sensitivity | ✓ | ~ | ~ |
| Search regex | ✓ | - | ~ |
| Native select support (required) | ✓ | - | ~ |
| Options definition* |
| Array | ✓ | ✓ | ✓ |
| Object | ✓ | - | - |
| Array of objects | ✓ | ✓ | ✓ |
| Function (async) | ✓ | - | - |
| Styling* |
| Override class names | ✓ | - | - |
| CSS vars support | ✓ | - | ✓ |
| Class based CSS support | ✓ | ✓ | ✓ |
|


Tailwind CSS support
| ✓ | - | - |
| Support* |
| Accessibility (a11y) | ✓ | ~ | ~ |
| Internationalization (i18n) | ✓ | ~ | ~ |
| RTL support | ✓ | ✓ | ✓ |
| Typescript support | ✓ | ✓ | ✓ |
| SSR support | ✓ | ✓ | ✓ |
| ES Module support (ESM) | ✓ | - | - |
| CSP compilant | ✓ | - | - |
| API* |
| Events | 12 | 7 | 11 |
| Slots | 14 | 12 | 11 |
| Documented API methods | 10 | 0 | 0 |
| Stats** |
| Minzipped size | 9.7 KB | 14.2 KB | 20.6 KB |
| Open issues | 18 | 233 | 191 |
| Monthly downloads | 180k | 1.1M | 947k |
| Dependencies | 0 | 0 | 0 |
| Coverage | 100% | unknown | 96% |
| Latest realease | 2022. 12. 21. | 2019. 04. 27. | 2022. 12. 18. |

**~** \- partial support / requires manual extension or official support is in progress
**\*** \- as of June 13, 2022 - reviewed periodically
**\*\*** \- as of Dec 21, 2022 - reviewed periodically

*Disclaimer: based on docs, Github issues and code discovery.*

## Docs

- [Sponsors](#sponsors)
- [Comparison with other libraries](#comparison-with-other-libraries)
- [Docs](#docs)
- [Demo](#demo)
- [Installation](#installation)
- [Using with Vue 3](#using-with-vue-3)
- [Using with Vue 2](#using-with-vue-2)
- [Using with \< Vue 2.7](#using-with--vue-27)
- [Support](#support)
- [Configuration](#configuration)
- [Basic props](#basic-props)
- [Advanced Props](#advanced-props)
- [API methods](#api-methods)
- [Events](#events)
- [Slots](#slots)
- [Styling](#styling)
- [Styling with CSS vars](#styling-with-css-vars)
- [Styling with Tailwind CSS](#styling-with-tailwind-css)
- [Using `:classes` prop](#using-classes-prop)
- [Examples](#examples)
- [Single select](#single-select)
- [Multiselect with object options](#multiselect-with-object-options)
- [Multiselect with disabled options](#multiselect-with-disabled-options)
- [Multiselect with groups](#multiselect-with-groups)
- [Tags with search, create and array of objects options](#tags-with-search-create-and-array-of-objects-options)
- [Autocomplete with async options](#autocomplete-with-async-options)
- [Tags with async options](#tags-with-async-options)
- [Select with custom options slot](#select-with-custom-options-slot)
- [Multiselect with custom label slot](#multiselect-with-custom-label-slot)
- [Tags with custom tags slot](#tags-with-custom-tags-slot)
- [Async options with default values](#async-options-with-default-values)
- [Default values that are not among the options using `object: true`](#default-values-that-are-not-among-the-options-using-object-true)
- [Default values that are not among the options using `allowAbsent: true`](#default-values-that-are-not-among-the-options-using-allowabsent-true)
- [Manage created tag asynchronously](#manage-created-tag-asynchronously)
- [Load async options from API on open with infinite scroll](#load-async-options-from-api-on-open-with-infinite-scroll)
- [Multiselect with localized texts](#multiselect-with-localized-texts)
- [License](#license)

## Demo

Check out our demo.

## Installation

```
npm install @vueform/multiselect
```

## Using with Vue 3

```vue



import Multiselect from '@vueform/multiselect'

export default {
components: {
Multiselect,
},
data() {
return {
value: null,
options: [
'Batman',
'Robin',
'Joker',
]
}
}
}

```

## Using with Vue 2

```vue



import Multiselect from '@vueform/multiselect/dist/multiselect.vue2.js'

export default {
components: {
Multiselect,
},
data() {
return {
value: null,
options: [
'Batman',
'Robin',
'Joker',
]
}
}
}

```

#### Using with < Vue 2.7

Switch to [`<= 2.4.2`](https://github.com/vueform/multiselect/tree/2.4.2) to use the Multiselect with Vue.js `< 2.7`.

## Support

Join our [Discord channel](https://discord.gg/WhX2nG6GTQ) or [open an issue](https://github.com/vueform/multiselect/issues).

## Configuration

### Basic props

| Name | {Type} Default | Description |
| --- | --- | --- |
| **mode** | `{string} 'single'` | Possible values: `'single'\|'multiple'\|'tags'`. |
| **options** | `{array\|object\|function} []` | List of options. Can be:
- an array (eg. `[1,2,3]`)
- an object (eg. `{a:1,b:2,c:3}`)
- an array of objects:
`[`
  `{`
    `[valueProp]: 1,`
    `[label]: 'v1',`
    `disabled:true\|false`
  `},`
  `//...`
`]`
- a function returning a Promise (async function) with `query` and `select$` param. The `select$` represents the Multiselect component and its API can be accessed. The promise should return options as an **object** or as an **array of objects**.
When an array of objects is provided it **must** have properties that equal to `:valueProp`'s, `:trackBy`'s and `:label`'s value. |
| **groups** | `{boolean} false` | Whether options should be grouped. Example:
`{`
  `groups: true,`
  `options: [`
    `{`
      `[groupLabel]: 'Group label',`
      `[groupOptions]: {options},`
      `disabled: true\|false,`
    `}`
    `//...`
  `]`
`}`
The `{options}` should equal to regular `options` definition. |
| **groupLabel** | `{string} 'label'` | The name of the property that contains the label of a group when `options` are provided in group format and `groups` is `true`. |
| **groupOptions** | `{string} 'options'` | The name of the property that contains the options of a group when `options` are provided in group format and `groups` is `true`. |
| **groupSelect** | `{boolean} true` | Whether groups can be selected when using `multiple` or `tags` mode. |
| **groupHideEmpty** | `{boolean} false` | Whether groups that have no `options` by default should be hidden. |
| **required** | `{boolean} false` | Whether the HTML5 required attribute should be used for multiselect (using an invisible fake input). |
| **infinite** | `{boolean} false` | Whether the actual option nodes should only be loaded on scroll. The `limit` option defines how many options are loaded initially and in each new batch. |
| **appendToBody** | `{boolean} false` | **[Vue 3 only]** *(experimental)* Whether the dropdown list should be appended to `` and positioned absolutely. |
| **appendTo** | `{string} undefined` | **[Vue 3 only]** *(experimental)* Can be used instead of `appendToBody` to teleport the dropdown to a specific DOM. The value should be a query selector. |
| **closeOnScroll** | `{boolean} false` | Closes the dropdown list on scrolling parent DOM / window when using `appendToBody: true`. |
| **searchable** | `{boolean} false` | Whether the options should be searchable. |
| **valueProp** | `{string} 'value'` | If you provide an array of objects as `options` this property should be used as the value of the option. |
| **trackBy** | `{string\|array} undefined` | The name(s) of the properties that should be searched when `searchable` is `true` and an array of objects are provided as `options`. If left `undefined` the `label` prop will be used instead. |
| **label** | `{string} 'label'` | If you provide an array of objects as `options` the value of this property will be displayed as selected option. |
| **disabledProp** | `{string} 'disabled'` | If you provide an array of objects as `options` this property should be used to determine whether the option is disabled. |
| **placeholder** | `{string} null` | The text that should be displayed before any option is selected. |
| **multipleLabel** | `function(value, select$)` | A function that returns the label to be displayed for selected options when using `multiple` mode. It receives `value` as first argument and the multiselect component `select$` as second. By default it renders `1 option selected` and `[n] options selected` based on `value` length. |
| **disabled** | `{boolean} false` | Whether the input should be disabled for the user (API can still be used programmatically). |
| **inputType** | `{string} 'text'` | The `type` attribute of the search input. |
| **autocomplete** | `{string} undefined` | The `autocomplete` attribute of the search input. |
| **rtl** | `{boolean} false` | Whether the multiselect should be right-to-left. It also respects `dir="rtl"` on any parent element. |
| **max** | `{number} -1` | The maximum number of options that **can be selected** when using `multiple` or `tags` mode. If `-1` the number of options won't be limited. |
| **limit** | `{number} -1` | The maximum number of options that **should be displayed**. If `-1` the number of options won't be limited. |
| **loading** | `{boolean} false` | Whether a loading spinner should be shown. |
| **id** | `{string} 'multiselect'` | The `id` of the multiselect container DOM. |
| **caret** | `{boolean} true` | Whether should display the caret symbol on the right. |
| **locale** | `{string} null` | The locale of the multiselect. If a locale is set labels might have an `object` value with different keys for different locales. |
| **locale** | `{string} 'en'` | The fallback locale. |
| **noOptionsText** | `{string\|object} 'The list is empty'` | The text that should be displayed when options list is empty. It can be an object with different keys for different locales. |
| **noResultsText** | `{string\|object} 'No results found'` | The text that should be displayed when there are no search results. It can be an object with different keys for different locales. |
| **openDirection** | `{string} 'bottom'` | Whether the option list should be displayed above or below the multiselect. Possible values: `top\|bottom` |
| **reverse** | `{boolean} false` | Whether the option list should be reversed. Only works with `groups: false`. |
| **regex** | `{regex\|string} undefined` | The regex that search input should be tested against when `searchable: true`. |
| **strict** | `{boolean} true` | Whether should regard accents/diacritics in search. |
| **searchStart** | `{boolean} false` | Whether the search should match the start of the options' `trackBy`s. |
| **searchFilter** | `function(option, query, select$) null` | A custom search function that overrides the default search algorithm. |
| **aria** | `object` | An object containing aria attributes to be added for the multiselect. |
| **classes** | `object` | An object of class names that gets merged with the default values. Default: `{`
  `container: 'multiselect',`
  `containerDisabled: 'is-disabled',`
  `containerOpen: 'is-open',`
  `containerOpenTop: 'is-open-top',`
  `containerActive: 'is-active',`
  `wrapper: 'multiselect-wrapper',`
  `singleLabel: 'multiselect-single-label',`
  `singleLabelText: 'multiselect-single-label-text',`
  `multipleLabel: 'multiselect-multiple-label',`
  `search: 'multiselect-search',`
  `tags: 'multiselect-tags',`
  `tag: 'multiselect-tag',`
  `tagDisabled: 'is-disabled',`
  `tagWrapper: 'multiselect-tag-wrapper',`
  `tagWrapperBreak: 'multiselect-tag-wrapper-break',`
  `tagRemove: 'multiselect-tag-remove',`
  `tagRemoveIcon: 'multiselect-tag-remove-icon',`
  `tagsSearchWrapper: 'multiselect-tags-search-wrapper',`
  `tagsSearch: 'multiselect-tags-search',`
  `tagsSearchCopy: 'multiselect-tags-search-copy',`
  `placeholder: 'multiselect-placeholder',`
  `caret: 'multiselect-caret',`
  `caretOpen: 'is-open',`
  `clear: 'multiselect-clear',`
  `clearIcon: 'multiselect-clear-icon',`
  `spinner: 'multiselect-spinner',`
  `infinite: 'multiselect-infinite',`
  `infiniteSpinner: 'multiselect-infinite-spinner',`
  `dropdown: 'multiselect-dropdown',`
  `dropdownTop: 'is-top',`
  `dropdownHidden: 'is-hidden',`
  `options: 'multiselect-options',`
  `optionsTop: 'is-top',`
  `group: 'multiselect-group',`
  `groupLabel: 'multiselect-group-label',`
  `groupLabelPointable: 'is-pointable',`
  `groupLabelPointed: 'is-pointed',`
  `groupLabelSelected: 'is-selected',`
  `groupLabelDisabled: 'is-disabled',`
  `groupLabelSelectedPointed: 'is-selected is-pointed',`
  `groupLabelSelectedDisabled: 'is-selected is-disabled',`
  `groupOptions: 'multiselect-group-options',`
  `option: 'multiselect-option',`
  `optionPointed: 'is-pointed',`
  `optionSelected: 'is-selected',`
  `optionDisabled: 'is-disabled',`
  `optionSelectedPointed: 'is-selected is-pointed',`
  `optionSelectedDisabled: 'is-selected is-disabled',`
  `noOptions: 'multiselect-no-options',`
  `noResults: 'multiselect-no-results',`
  `fakeInput: 'multiselect-fake-input',`
  `assist: 'multiselect-assistive-text'`
  `spacer: 'multiselect-spacer'`
`}` |


Vueform

### Advanced Props

| Name | {Type} Default | Description |
| --- | --- | --- |
| **allowAbsent** | `{boolean} false` | Whether values should be allowed which are not part of options even when using `object: false`. The selected values which are not part of the option list will have the same value and label. This can be useful if you're using an async option list with an array of string options as a result where both labels and values will be the same and you want to have default values which are not part of the initially resolved options. [Example #13](#example-13) |
| **canDeselect** | `{boolean} true` | Whether a selected option can be deselected when using `single` mode. |
| **canClear** | `{boolean} true` | Whether option(s) can be cleared. |
| **clearOnSearch** | `{boolean} false` | Whether the option list should be cleared when a new character is typed before loading new options list, when using async options. |
| **clearOnSelect** | `{boolean} true` | Whether the option list should be cleared upon selecting an option when using async options. |
| **closeOnSelect** | `{boolean} true` | Whether the option list should be closed upon selecting an option. |
| **closeOnDeselect** | `{boolean} true` | Whether the option list should be closed upon deselecting an option. |
| **clearOnBlur** | `{boolean} true` | Whether the search should be cleared when the input is blurred when `searchable: true`. |
| **delay** | `{number} -1` | The delay in milliseconds that should occur between the last typed character and refreshing an async option list. If `-1` the option list will not refresh when the search query changes. If `0` it will refresh without delay. |
| **filterResults** | `{boolean} true` | Whether option list should be filtered by search query. This may be set to `false` if you are handling filtering manually when returning async options. |
| **minChars** | `{number} 0` | The minimum number of characters that should be typed to refresh async option list. If `0` it will refresh even when the search field becomes empty. |
| **resolveOnLoad** | `{boolean} true` | Whether async options should be loaded initially (with an empty query). This should be `true` if you are planning to load non-object value(s) initially while using async options (to fetch matching objects for values). |
| **breakTags** | `{boolean} false` | Whether long tags should be broken into multiple lines (otherwise truncated at max width). |
| **appendNewTag** | `{boolean} true` | **Deprecated 2.3.0: use `appendNewOption` instead.**
Whether it should append new tag automatically to option list when using `tags` mode with `createTag`. If set to `false` you need to take care of appending a new tag to the provided `:options` list upon `@tag` event. |
| **createTag** | `{boolean} false` | **Deprecated 2.3.0: use `createOption` instead.**
Whether it should allow creating new tags based on search query when using `tags` mode. |
| **addTagOn** | `{array} ['enter']` | **Deprecated 2.3.0: use `addOptionOn` instead.**
The list of keys that creates a new tag while typing in the search field when having `createTag` enabled. Possible values: `'enter'\|'space'\|'tab'\|';'\|','`. |
| **appendNewOption** | `{boolean} true` | Whether it should append new option automatically to option list when `searchable` and `createTag` are enabled. If set to `false` you need to take care of appending a new option to the provided `:options` list upon `@option` event. |
| **createOption** | `{boolean} false` | Whether it should allow creating new options based on search query when `searchable` is enabled. |
| **addOptionOn** | `{array} ['enter']` | The list of keys that creates a new option while typing in the search field when having `createOption` enabled. Possible values: `'enter'\|'space'\|'tab'\|';'\|','`. |
| **onCreate** | `function(option, select$)` | Transforms the created tag before being added when `createOption` is enabled. It receives the original `option` as first param, which is the object that would be added to the option list (`{value: 'Value', label: 'Label'}`) and the Multiselect `component` as the second. It should return an object that contains at least the keys defined by `valueProp`, `label` & `trackBy` options. If defined and returns `false` the option will not be added (the add can be handled manually by updating `options` & `v-model`). |
| **hideSelected** | `{boolean} true` | Whether selected options should be excluded from the option list when using `multiple` or `tags` mode. |
| **showOptions** | `{boolean} true` | Whether option list should be displayed. Can be used to create free-typed tags. |
| **object** | `{boolean} false` | Whether the value should be stored as an object.
If **false**:
`value: ['js','jsx','ts']`
If **true**:
`value: [`
  `{value:'js',label:'Javascript'},`
  `{value:'jsx',label:'JSX'},`
  `{value:'ts',label:'Typescript'}`
`]` |
| **attrs** | `{object} {}` | HTML attributes to add to the `input` field when `search` is enabled. |
| **nativeSupport** | `{boolean} false` | Whether hidden input fields should be appended to achieve native data handling. |

## API methods

| Name | Params | Description |
| --- | --- | --- |
| **open** | | Opens the options list. |
| **close** | | Closes the options list. |
| **select** | `option` | Selects an option based on its value. |
| **deselect** | `option` | Deselects an option based on its value. |
| **remove** | `option` | Alias for `deselect`. |
| **selectAll** | | Selects all options if mode is `tags` or `multiple`. |
| **clear** | | Deselects all selected options. |
| **clearSearch** | | Clears current search query. |
| **refreshOptions** | `callback` | Refreshes async options list. |
| **setPointer** | `option` | Points an option based on its value. |

To access API use `ref` on `Multiselect` component:
``` html

```

``` js
// eg:
mounted() {
this.$refs.multiselect.open()
}
```

To programmatically open and focus the multiselect, call `focus()` on the element:

```js
mounted() {
this.$refs.multiselect.$el.focus()
}
```

## Events

| Event | Attributes | Description |
| --- | --- | --- |
| **@change** | `value, select$` | Emitted after the value is changed. |
| **@select** | `value, option, select$` | Emitted after an option or tag is selected. |
| **@deselect** | `value, option, select$` | Emitted after an option is deselected or a tag is removed. |
| **@open** | `select$` | Emitted after opening the option list. |
| **@close** | `select$` | Emitted after closing the option list. |
| **@search-change** | `query, select$` | Emitted after a character is typed. |
| **@tag** | `query, select$` | **Deprecated 2.3.0: use `@create` instead**. Emitted after enter is hit when a new tag is being created. |
| **@option** | `query, select$` | **Deprecated 2.6.0: use `@create` instead**. Emitted after enter is hit when a new option is being created. |
| **@create** | `query, select$` | Emitted after enter is hit when a new option is being created. |
| **@clear** | `select$` | Emitted when the options are cleared. |
| **@paste** | `Event, select$` | Emitted when value is pasted into the search field. |
| **@keydown** | `Event, select$` | Emitted on `keydown`. |
| **@keyup** | `Event, select$` | Emitted on `keyup`. |
| **@max** | `select$` | Emitted when `max` is reached when in `multiple` or `tags` mode. |

The `select$` param in each event is the Multiselect component's instance.

## Slots

| Slot | Attributes | Description |
| --- | --- | --- |
| **placeholder** | | Rendered as placeholder when the multiselect does not have value and `placeholder` prop is defined. |
| **afterlist** | | Rendered after the options list. |
| **beforelist** | | Rendered before the options list. |
| **multiplelabel** | `values` | Rendered when using `multiple` mode and options are selected. By default it renders the return value of `multipleLabel` function. |
| **nooptions** | | Rendered when the options list is empty. By default renders `noOptionsText`. |
| **noresults** | | Rendered when there are no search results. By default renders `noResultsText`. |
| **grouplabel** | `group, isPointed, isSelected` | Renders an option group label. The `isPointed` and `isSelected` props are function that used be used like `isPointed(option)` to determine the state. |
| **option** | `option, isPointed, isSelected, search` | Renders an option in options list. The `isPointed` and `isSelected` props are function that used be used like `isPointed(option)` to determine the state. |
| **singlelabel** | `value` | Rendered when using `single` mode and an option is selected. By default it renders the `:label` if the selected option. |
| **tag** | `option, handleTagRemove, disabled` | Renders a tag when using `tags` mode. When `disabled` the remove icon should not be displayed. The `handleTagRemove` prop should be used to trigger the removal of the tag. |
| **caret** | `handleCaretClick, isOpen` | Renders a small triangle on the right side of the multiselect. The content of the slot should have `pointer-events: false` when `isOpen: false` to replicate the original behaviour. |
| **clear** | `clear` | Renders a remove icon if the multiselect has any value. The `clear` method should be used on `mousedown` event. |
| **spinner** | | Renders a loader icon when async options are being fetched. |
| **infinite** | | Renders a loader icon when infinite scroll is in progress. |

> Note: we don't use camelCase because they are [normalized back to lowercase](https://github.com/vuejs/vue/issues/9449#issuecomment-461170017) when written in DOM.

## Styling

### Styling with CSS vars

The following CSS variables can be used to customize multiselect when using `default.css`:

``` css
--ms-font-size: 1rem;
--ms-line-height: 1.375;
--ms-bg: #FFFFFF;
--ms-bg-disabled: #F3F4F6;
--ms-border-color: #D1D5DB;
--ms-border-width: 1px;
--ms-border-color-active: #D1D5DB;
--ms-border-width-active: 1px;
--ms-radius: 4px;
--ms-py: 0.5rem;
--ms-px: 0.875rem;
--ms-ring-width: 3px;
--ms-ring-color: #10B98130;
--ms-placeholder-color: #9CA3AF;
--ms-max-height: 10rem;

--ms-spinner-color: #10B981;
--ms-caret-color: #999999;
--ms-clear-color: #999999;
--ms-clear-color-hover: #000000;

--ms-tag-font-size: 0.875rem;
--ms-tag-line-height: 1.25rem;
--ms-tag-font-weight: 600;
--ms-tag-bg: #10B981;
--ms-tag-bg-disabled: #9CA3AF;
--ms-tag-color: #FFFFFF;
--ms-tag-color-disabled: #FFFFFF;
--ms-tag-radius: 4px;
--ms-tag-py: 0.125rem;
--ms-tag-px: 0.5rem;
--ms-tag-my: 0.25rem;
--ms-tag-mx: 0.25rem;

--ms-tag-remove-radius: 4px;
--ms-tag-remove-py: 0.25rem;
--ms-tag-remove-px: 0.25rem;
--ms-tag-remove-my: 0rem;
--ms-tag-remove-mx: 0.125rem;

--ms-dropdown-bg: #FFFFFF;
--ms-dropdown-border-color: #D1D5DB;
--ms-dropdown-border-width: 1px;
--ms-dropdown-radius: 4px;

--ms-group-label-py: 0.3rem;
--ms-group-label-px: 0.75rem;
--ms-group-label-line-height: 1.375;
--ms-group-label-bg: #E5E7EB;
--ms-group-label-color: #374151;
--ms-group-label-bg-pointed: #D1D5DB;
--ms-group-label-color-pointed: #374151;
--ms-group-label-bg-disabled: #F3F4F6;
--ms-group-label-color-disabled: #D1D5DB;
--ms-group-label-bg-selected: #059669;
--ms-group-label-color-selected: #FFFFFF;
--ms-group-label-bg-selected-pointed: #0c9e70;
--ms-group-label-color-selected-pointed: #FFFFFF;
--ms-group-label-bg-selected-disabled: #75cfb1;
--ms-group-label-color-selected-disabled: #D1FAE5;

--ms-option-font-size: 1rem;
--ms-option-line-height: 1.375;
--ms-option-bg-pointed: #FFFFFF;
--ms-option-color-pointed: #1F2937;
--ms-option-bg-selected: #10B981;
--ms-option-color-selected: #FFFFFF;
--ms-option-bg-disabled: #FFFFFF;
--ms-option-color-disabled: #D1D5DB;
--ms-option-bg-selected-pointed: #26C08E;
--ms-option-color-selected-pointed: #FFFFFF;
--ms-option-bg-selected-disabled: #FFFFFF;
--ms-option-color-selected-disabled: #D1FAE5;
--ms-option-py: 0.5rem;
--ms-option-px: 0.75rem;

--ms-empty-color: #4B5563;
```

Override them globally:

``` css
:root {
--ms-tag-bg: #059669;
--ms-tag-color: #D1FAE5;
--ms-tag-radius: 9999px;
--ms-tag-font-weight: 400;
}
```

Or on an instance level:

```vue

```

``` css
.multiselect-green {
--ms-tag-bg: #D1FAE5;
--ms-tag-color: #059669;
}

.multiselect-blue {
--ms-tag-bg: #DBEAFE;
--ms-tag-color: #2563EB;
}
```

### Styling with Tailwind CSS

To use `Multiselect` with Tailwind CSS first you need install `npm i -D mini-svg-data-uri` and add background images to `tailwind.config.js`:

``` js
// tailwind.config.js

const svgToDataUri = require('mini-svg-data-uri')

module.exports = {
theme: {
extend: {
backgroundImage: (theme) => ({
'multiselect-caret': `url("${svgToDataUri(
``,
)}")`,
'multiselect-spinner': `url("${svgToDataUri(
``,
)}")`,
'multiselect-remove': `url("${svgToDataUri(
``,
)}")`,
})
},
}
}
```

Then you need to import `themes/tailwind.css` to you main component:

```vue



// ...

@import '@vueform/multiselect/themes/tailwind.css';
/* or */
/* @import './path/to/node_modules/@vueform/multiselect/themes/tailwind.css'; */

```

#### Using `:classes` prop

Alternatively you can define class names directly by passing them to the `Multiselect` component via `classes` property. When using this approach you don't need to import `tailwind.css`. Here's a default styling for Tailwind CSS (the same included in `tailwind.css`):

```vue

```

Certain classes has different states which are merged to the base class when the state is active. For example `dropdown` will be merged with `dropdownTop` when `open-direction: 'top'` resulting in the following classes:
```absolute -left-px -right-px bottom-0 transform translate-y-full border border-gray-300 -mt-px overflow-y-scroll z-50 bg-white flex flex-col rounded-b -translate-y-full top-px bottom-auto rounded-b-none rounded-t```

The same is true for `container`, `tag`, `options`, `groupLabel` and `option` classes.

In case you need to override the same type of utility you might use [@neojp/tailwind-important-variant](https://www.npmjs.com/package/@neojp/tailwindcss-important-variant) for eg. `bg-green-500!`.

## Examples

- [Single select](#single-select)
- [Multiselect with object options](#multiselect-with-object-options)
- [Multiselect with disabled options](#multiselect-with-disabled-options)
- [Multiselect with groups](#multiselect-with-groups)
- [Tags with search, create and array of objects options](#tags-with-search-create-and-array-of-objects-options)
- [Autocomplete with async options](#autocomplete-with-async-options)
- [Tags with async options](#tags-with-async-options)
- [Select with custom options slot](#select-with-custom-options-slot)
- [Multiselect with custom label slot](#multiselect-with-custom-label-slot)
- [Tags with custom tags slot](#tags-with-custom-tags-slot)
- [Async options with default values](#async-options-with-default-values)
- [Default values that are not among the options](#default-values-that-are-not-among-the-options)
- [Manage created tag asynchronously](#manage-created-tag-asynchronously)
- [Load async options from API on open with infinite scroll](#load-async-options-from-api-on-open-with-infinite-scroll)

### Single select

```vue

```

JSFiddle - Example #1

### Multiselect with object options

```vue

```

JSFiddle - Example #2

### Multiselect with disabled options

```vue

```

JSFiddle - Example #3

### Multiselect with groups

```vue

```

JSFiddle - Example #4

### Tags with search, create and array of objects options

```vue

```

JSFiddle - Example #5

### Autocomplete with async options

```vue

```

JSFiddle - Example #6

### Tags with async options

```vue

```

JSFiddle - Example #7

### Select with custom options slot

```vue



{{ value.name }}


{{ option.name }}

```

JSFiddle - Example #8

### Multiselect with custom label slot

```vue



{{ values.length }} characters selected

```

JSFiddle - Example #9

### Tags with custom tags slot

```vue





{{ option.name }}





.multiselect-tag.is-user {
padding: 5px 8px;
border-radius: 22px;
background: #35495e;
margin: 3px 3px 8px;
}

.multiselect-tag.is-user img {
width: 18px;
border-radius: 50%;
height: 18px;
margin-right: 8px;
border: 2px solid #ffffffbf;
}

.multiselect-tag.is-user i:before {
color: #ffffff;
border-radius: 50%;;
}

.user-image {
margin: 0 6px 0 0;
border-radius: 50%;
height: 22px;
}

```

JSFiddle - Example #10

### Async options with default values

When using `resolveOnLoad: false` we can add default values with `object: true` and providing options as objects, containing both `label` and `value` props. This is because option list is not resolved when the component is mounted so Multiselect has no idea of what option labels should be if only plain values were provided.

```vue

export default {
data: () => ({
value: [
{ value: 'Java', label: 'Java' },
{ value: 'JavaScript', label: 'JavaScript' },
]
})
}

```

JSFiddle - Example #11

### Default values that are not among the options using `object: true`

If we want to add default values without having to add them to options list we can use `object: true` and provide them as objects, containing both `label` and `value` props. This is because if a plain value is not among Multiselect options it has no idea of what option label should be.

```vue

export default {
data: () => ({
value: [
{ value: 'Java', label: 'Java' },
{ value: 'JavaScript', label: 'JavaScript' },
]
})
}

```

JSFiddle - Example #12

### Default values that are not among the options using `allowAbsent: true`

If our async option list returns an **array of strings** we can use `allowAbsent: true` to allow value(s) which are not among the option list. The reason why this only works with an array of strings option list is because plain values like `Java` and `JavaScript` will use the same string for label and value.

```vue

export default {
data: () => ({
value: [
'Java',
'JavaScript',
]
})
}

```

JSFiddle - Example #13

### Manage created tag asynchronously

Search is restricted by `regex` and tag creation is controlled by `onCreate(option, select$)`.

```vue

export default {
methods: {
handleTagCreate: async (option, select$) => {
// Do not allow create tags above 67
if (parseInt(option.value) > 67) {
alert(`${option.value} is not allowed. Option must by <= 67.`)

// If returns `false` the tag will not be added
return false
}

// Async request (eg. for validating)
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})

// Modifying option label
option.label = option.label + ' - confirmed'

return option
}
}
}

```

JSFiddle - Example #14

### Load async options from API on open with infinite scroll

Options are not loaded initially, only when the users clicks the dropdown the first time. It also virtualizes the option list with `infinite: true` even large list of options can be loaded.

```vue
{
if (select$.noOptions) {
select$.resolveOptions()
}
}"
/>
```

JSFiddle - Example #15

### Multiselect with localized texts

Options are not loaded initially, only when the users clicks the dropdown the first time. It also virtualizes the option list with `infinite: true` even large list of options can be loaded.

```vue

export default {
data: () => ({
value: [1]
})
}

```

JSFiddle - Example #16

## License

[MIT](https://github.com/vueform/multiselect/blob/main/LICENSE.md)