Vue 3 compatible multiselect component with advanced selection, search, tagging, and grouping capabilities
—
Extensive configuration options for behavior control, limits, keyboard handling, and UI customization.
Control the maximum number of selections and display limits.
/**
* Selection limit configuration props
*/
interface SelectionLimitProps {
/** Maximum number of selections allowed (0 = unlimited, default: false) */
max?: number | boolean;
/** Maximum number of selected options to display (default: 99999) */
limit?: number;
/**
* Function to generate text for excess selections
* @param count - Number of selections beyond the limit
* @returns Formatted string for display
*/
limitText?: (count: number) => string;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedTags"
:options="availableTags"
:multiple="true"
:max="5"
:limit="3"
:limit-text="formatLimitText"
placeholder="Select up to 5 tags">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedTags: [],
availableTags: ['JavaScript', 'Vue.js', 'React', 'Angular', 'TypeScript', 'Node.js']
}
},
methods: {
formatLimitText(count) {
return `and ${count} more tag${count === 1 ? '' : 's'}`;
}
}
}
</script>Configure component behavior for various user interactions.
/**
* Behavior control configuration props
*/
interface BehaviorControlProps {
/** Close dropdown after selecting an option (default: true) */
closeOnSelect?: boolean;
/** Clear search input after selecting an option (default: true) */
clearOnSelect?: boolean;
/** Hide already selected options from dropdown (default: false) */
hideSelected?: boolean;
/** Allow clearing all selections (default: true) */
allowEmpty?: boolean;
/** Reset component state after selection (stateless mode, default: false) */
resetAfter?: boolean;
/** Auto-select first option when no value is set (default: false) */
preselectFirst?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedSkills"
:options="skills"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:hide-selected="true"
:allow-empty="true"
placeholder="Add your skills">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedSkills: [],
skills: ['JavaScript', 'Python', 'Java', 'C++', 'Go', 'Rust']
}
}
}
</script>Display loading indicators during async operations.
/**
* Loading state configuration props
*/
interface LoadingStateProps {
/** Show loading spinner (default: false) */
loading?: boolean;
/** Disable component during loading */
disabled?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedUser"
:options="users"
:loading="isLoading"
:disabled="isLoading"
@search-change="searchUsers"
:internal-search="false"
placeholder="Search users...">
<template #loading>
<div class="custom-loading">
<span>Searching users...</span>
</div>
</template>
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedUser: null,
users: [],
isLoading: false
}
},
methods: {
async searchUsers(query) {
if (!query) return;
this.isLoading = true;
try {
const response = await fetch(`/api/users/search?q=${query}`);
this.users = await response.json();
} finally {
this.isLoading = false;
}
}
}
}
</script>Configure keyboard behavior and shortcuts.
/**
* Keyboard navigation configuration props
*/
interface KeyboardNavigationProps {
/** Array of keyboard keys to block during selection */
blockKeys?: string[];
/** Prevent automatic focus when component activates (default: false) */
preventAutofocus?: boolean;
/** HTML tabindex for keyboard navigation order */
tabindex?: number;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedOption"
:options="options"
:block-keys="['Delete', 'Backspace']"
:prevent-autofocus="true"
:tabindex="2"
placeholder="Keyboard navigation disabled">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedOption: null,
options: ['Option 1', 'Option 2', 'Option 3']
}
}
}
</script>Customize the visual presentation and layout.
/**
* Display customization configuration props
*/
interface DisplayCustomizationProps {
/** Maximum height of dropdown in pixels (default: 300) */
maxHeight?: number;
/** Force dropdown opening direction ('above' | 'below' | '', default: '') */
openDirection?: string;
/** Show labels for keyboard interactions (default: true) */
showLabels?: boolean;
/** Show "No options" message when options array is empty (default: true) */
showNoOptions?: boolean;
/** Show "No results" message when search yields no results (default: true) */
showNoResults?: boolean;
/** Use Vue 3 Teleport for dropdown positioning (default: false) */
useTeleport?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedItem"
:options="largeOptionsList"
:max-height="200"
open-direction="above"
:show-labels="false"
:use-teleport="true"
placeholder="Customized dropdown">
<template #noOptions>
<div class="custom-no-options">
<i class="icon-search"></i>
<p>No items available</p>
</div>
</template>
<template #noResult="{ search }">
<div class="custom-no-results">
<p>No results found for "{{ search }}"</p>
<button @click="clearSearch">Clear search</button>
</div>
</template>
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedItem: null,
largeOptionsList: Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)
}
},
methods: {
clearSearch() {
// Clear search functionality
this.$refs.multiselect.updateSearch('');
}
}
}
</script>Configure options for optimal performance with large datasets.
/**
* Performance optimization configuration props
*/
interface PerformanceOptimizationProps {
/** Maximum number of options to display in dropdown (default: 1000) */
optionsLimit?: number;
/** Disable internal search for async/custom filtering (default: true) */
internalSearch?: boolean;
/** Custom sorting function for search results */
filteringSortFunc?: (a: any, b: any) => number;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedCity"
:options="filteredCities"
:options-limit="50"
:internal-search="false"
:filtering-sort-func="sortByPopulation"
@search-change="debounceSearch"
placeholder="Search cities...">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedCity: null,
allCities: [], // Large dataset of cities
filteredCities: [],
searchTimeout: null
}
},
methods: {
debounceSearch(query) {
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => {
this.performSearch(query);
}, 300);
},
performSearch(query) {
if (!query) {
this.filteredCities = this.allCities.slice(0, 50);
return;
}
this.filteredCities = this.allCities
.filter(city => city.name.toLowerCase().includes(query.toLowerCase()))
.slice(0, 50);
},
sortByPopulation(a, b) {
return b.population - a.population;
}
}
}
</script>Control dropdown navigation highlighting and option height calculations.
/**
* Navigation and pointer configuration props
*/
interface NavigationPointerProps {
/** Enable/disable highlighting of the pointed value (default: true) */
showPointer?: boolean;
/** Height of each option in pixels for scroll calculations (default: 40) */
optionHeight?: number;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedOption"
:options="longOptionsList"
:show-pointer="true"
:option-height="35"
placeholder="Navigate with arrow keys">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedOption: null,
longOptionsList: Array.from({ length: 1000 }, (_, i) => `Option ${i + 1}`)
}
}
}
</script>Configure automatic state reset for stateless usage patterns.
/**
* State reset configuration props
*/
interface StateResetProps {
/** Reset internal state after each selection (default: false) */
resetAfter?: boolean;
/** Preserve search value across selections (default: false) */
preserveSearch?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="currentSelection"
:options="dynamicOptions"
:reset-after="true"
:preserve-search="false"
@select="handleSelection"
placeholder="Stateless selector">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
currentSelection: null,
dynamicOptions: [],
selectionHistory: []
}
},
methods: {
handleSelection(selected) {
// Process selection
this.selectionHistory.push(selected);
// Update options based on selection
this.updateOptionsForNextSelection(selected);
},
updateOptionsForNextSelection(lastSelected) {
// Logic to update options based on previous selection
this.dynamicOptions = this.getNextOptions(lastSelected);
}
}
}
</script>Configure accessibility features and ARIA attributes.
/**
* Accessibility configuration props
*/
interface AccessibilityProps {
/** HTML required attribute when no value selected */
required?: boolean;
/** Tab index for keyboard navigation */
tabindex?: number;
/** Component name for form submission and ARIA labels */
name?: string;
/** Enable spellcheck on search input */
spellcheck?: boolean;
/** Show pointer/selection labels for screen readers */
showLabels?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedAccessibleOption"
:options="accessibleOptions"
:required="true"
:tabindex="1"
name="accessible-select"
:spellcheck="true"
:show-labels="true"
select-label="Press enter to select this option"
deselect-label="Press enter to remove this option"
placeholder="Accessible multiselect">
</VueMultiselect>
</template>Configure component identification for complex event handling scenarios.
/**
* Event handling configuration props
*/
interface EventHandlingProps {
/**
* Unique identifier passed with all events for component identification
* Useful when multiple multiselect components exist
*/
id?: string | number | null;
}
/**
* All events include the component ID as second parameter
*/
interface EventHandlingEvents {
'@update:modelValue': (value: any, id: string | number) => void;
'@select': (option: any, id: string | number) => void;
'@remove': (option: any, id: string | number) => void;
'@search-change': (query: string, id: string | number) => void;
'@open': (id: string | number) => void;
'@close': (value: any, id: string | number) => void;
'@tag': (query: string, id: string | number) => void;
}Usage Example:
<template>
<div>
<VueMultiselect
v-model="userSelection"
:options="users"
id="user-selector"
@select="handleSelect"
@remove="handleRemove">
</VueMultiselect>
<VueMultiselect
v-model="roleSelection"
:options="roles"
id="role-selector"
@select="handleSelect"
@remove="handleRemove">
</VueMultiselect>
</div>
</template>
<script>
export default {
methods: {
handleSelect(option, componentId) {
console.log(`Selected ${option.name} from ${componentId}`);
if (componentId === 'user-selector') {
this.handleUserSelection(option);
} else if (componentId === 'role-selector') {
this.handleRoleSelection(option);
}
},
handleRemove(option, componentId) {
console.log(`Removed ${option.name} from ${componentId}`);
}
}
}
</script>Install with Tessl CLI
npx tessl i tessl/npm-vue-multiselect