Vue 3 compatible multiselect component with advanced selection, search, tagging, and grouping capabilities
—
Support for hierarchical option structures with group selection capabilities and custom group rendering.
Organize options into hierarchical groups with collapsible sections.
/**
* Basic grouping configuration props
*/
interface BasicGroupingProps {
/** Property name containing the group's options array */
groupValues?: string;
/** Property name containing the group's display label */
groupLabel?: string;
/** Enable selection of entire groups at once (default: false) */
groupSelect?: boolean;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedEmployees"
:options="departmentGroups"
:multiple="true"
:group-values="'employees'"
:group-label="'department'"
:group-select="true"
label="name"
track-by="id">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedEmployees: [],
departmentGroups: [
{
department: 'Engineering',
employees: [
{ id: 1, name: 'John Doe', role: 'Frontend Developer' },
{ id: 2, name: 'Jane Smith', role: 'Backend Developer' }
]
},
{
department: 'Design',
employees: [
{ id: 3, name: 'Alice Johnson', role: 'UX Designer' },
{ id: 4, name: 'Bob Wilson', role: 'Visual Designer' }
]
}
]
}
}
}
</script>Enable selection of entire groups with a single click.
/**
* Group selection configuration props
*/
interface GroupSelectionProps {
/** Enable group selection functionality */
groupSelect: true;
/** Label displayed for group selection action */
selectGroupLabel?: string;
/** Label displayed for group deselection action */
deselectGroupLabel?: string;
}Usage Example:
<template>
<VueMultiselect
v-model="selectedPermissions"
:options="permissionGroups"
:multiple="true"
:group-values="'permissions'"
:group-label="'category'"
:group-select="true"
select-group-label="Select all permissions in this category"
deselect-group-label="Remove all permissions in this category"
label="name"
track-by="id">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedPermissions: [],
permissionGroups: [
{
category: 'User Management',
permissions: [
{ id: 'user.create', name: 'Create Users' },
{ id: 'user.edit', name: 'Edit Users' },
{ id: 'user.delete', name: 'Delete Users' }
]
},
{
category: 'Content Management',
permissions: [
{ id: 'content.create', name: 'Create Content' },
{ id: 'content.publish', name: 'Publish Content' }
]
}
]
}
}
}
</script>Methods for managing group selection state.
/**
* Group management methods
*/
interface GroupManagementMethods {
/** Select all options in a specific group */
selectGroup(group: any): void;
/** Check if all options in a group are selected */
wholeGroupSelected(group: any): boolean;
/** Check if all options in a group are disabled */
wholeGroupDisabled(group: any): boolean;
/** Get CSS classes for group highlighting */
groupHighlight(index: number, group: any): string;
}Handle complex nested group structures with metadata.
<template>
<VueMultiselect
v-model="selectedCourses"
:options="courseGroups"
:multiple="true"
:group-values="'courses'"
:group-label="'subject'"
:group-select="true"
label="title"
track-by="code"
placeholder="Select courses">
<template #option="{ option }">
<div class="course-option">
<span class="course-title">{{ option.title }}</span>
<span class="course-credits">{{ option.credits }} credits</span>
<span class="course-level">Level {{ option.level }}</span>
</div>
</template>
<template #tag="{ option, remove }">
<span class="course-tag">
{{ option.code }}: {{ option.title }}
<button @click="remove(option)">×</button>
</span>
</template>
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedCourses: [],
courseGroups: [
{
subject: 'Computer Science',
totalCredits: 24,
courses: [
{
code: 'CS101',
title: 'Introduction to Programming',
credits: 4,
level: 1,
prerequisites: []
},
{
code: 'CS201',
title: 'Data Structures',
credits: 4,
level: 2,
prerequisites: ['CS101']
}
]
},
{
subject: 'Mathematics',
totalCredits: 16,
courses: [
{
code: 'MATH101',
title: 'Calculus I',
credits: 4,
level: 1,
prerequisites: []
},
{
code: 'MATH201',
title: 'Linear Algebra',
credits: 4,
level: 2,
prerequisites: ['MATH101']
}
]
}
]
}
}
}
</script>
<style>
.course-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 0;
}
.course-title {
font-weight: 500;
}
.course-credits, .course-level {
font-size: 12px;
color: #666;
}
.course-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 6px;
background: #e3f2fd;
border-radius: 3px;
font-size: 12px;
}
</style>Apply search and filtering to grouped options.
<template>
<VueMultiselect
v-model="selectedProducts"
:options="productGroups"
:multiple="true"
:searchable="true"
:group-values="'products'"
:group-label="'category'"
:group-select="true"
label="name"
track-by="sku"
placeholder="Search products by category">
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedProducts: [],
productGroups: [
{
category: 'Electronics',
products: [
{ sku: 'EL001', name: 'Smartphone', price: 699 },
{ sku: 'EL002', name: 'Laptop', price: 1299 },
{ sku: 'EL003', name: 'Headphones', price: 199 }
]
},
{
category: 'Books',
products: [
{ sku: 'BK001', name: 'JavaScript Guide', price: 39 },
{ sku: 'BK002', name: 'Vue.js Cookbook', price: 45 }
]
}
]
}
}
}
</script>Customize the appearance of group headers and options.
<template>
<VueMultiselect
v-model="selectedTeamMembers"
:options="teams"
:multiple="true"
:group-values="'members'"
:group-label="'teamName'"
:group-select="true"
label="name"
track-by="id">
<template #beforeList>
<div class="team-stats">
<strong>{{ selectedTeamMembers.length }}</strong> members selected
</div>
</template>
<template #option="{ option, search }">
<div class="team-member-option">
<img :src="option.avatar" :alt="option.name" class="member-avatar">
<div class="member-info">
<div class="member-name">{{ option.name }}</div>
<div class="member-role">{{ option.role }}</div>
<div class="member-email">{{ option.email }}</div>
</div>
<div class="member-status" :class="option.status">
{{ option.status }}
</div>
</div>
</template>
<template #tag="{ option, remove }">
<span class="member-tag">
<img :src="option.avatar" :alt="option.name" class="tag-avatar">
{{ option.name }}
<button @click="remove(option)" class="remove-btn">×</button>
</span>
</template>
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedTeamMembers: [],
teams: [
{
teamName: 'Frontend Team',
teamLead: 'Alice Johnson',
members: [
{
id: 1,
name: 'Alice Johnson',
role: 'Team Lead',
email: 'alice@company.com',
avatar: '/avatars/alice.jpg',
status: 'online'
},
{
id: 2,
name: 'Bob Smith',
role: 'Senior Developer',
email: 'bob@company.com',
avatar: '/avatars/bob.jpg',
status: 'away'
}
]
},
{
teamName: 'Backend Team',
teamLead: 'Charlie Brown',
members: [
{
id: 3,
name: 'Charlie Brown',
role: 'Team Lead',
email: 'charlie@company.com',
avatar: '/avatars/charlie.jpg',
status: 'online'
}
]
}
]
}
}
}
</script>
<style>
.team-stats {
padding: 8px 12px;
background: #f5f5f5;
border-bottom: 1px solid #ddd;
font-size: 12px;
}
.team-member-option {
display: flex;
align-items: center;
padding: 8px 12px;
gap: 12px;
}
.member-avatar, .tag-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
}
.tag-avatar {
width: 20px;
height: 20px;
}
.member-info {
flex: 1;
}
.member-name {
font-weight: 500;
font-size: 14px;
}
.member-role {
font-size: 12px;
color: #666;
}
.member-email {
font-size: 11px;
color: #999;
}
.member-status {
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
text-transform: uppercase;
}
.member-status.online {
background: #4caf50;
color: white;
}
.member-status.away {
background: #ff9800;
color: white;
}
.member-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
background: #e3f2fd;
border-radius: 16px;
font-size: 12px;
}
.remove-btn {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
color: #666;
padding: 0;
margin-left: 4px;
}
</style>Events specific to group operations.
/**
* Group-related events
*/
interface GroupEvents {
/** Emitted when a group is selected/deselected */
'@group-select': (group: any, selectedMembers: any[]) => void;
/** Emitted when group selection state changes */
'@group-change': (group: any, isSelected: boolean) => void;
}Handle disabled state for entire groups or individual options within groups.
<template>
<VueMultiselect
v-model="selectedFeatures"
:options="featureGroups"
:multiple="true"
:group-values="'features'"
:group-label="'plan'"
:group-select="true"
label="name"
track-by="id">
<template #option="{ option }">
<div class="feature-option" :class="{ disabled: option.disabled }">
<span>{{ option.name }}</span>
<span v-if="option.disabled" class="disabled-reason">
({{ option.disabledReason }})
</span>
</div>
</template>
</VueMultiselect>
</template>
<script>
export default {
data() {
return {
selectedFeatures: [],
featureGroups: [
{
plan: 'Basic Plan',
features: [
{ id: 1, name: 'Email Support', disabled: false },
{ id: 2, name: 'Basic Analytics', disabled: false }
]
},
{
plan: 'Premium Plan',
features: [
{
id: 3,
name: 'Priority Support',
disabled: true,
disabledReason: 'Upgrade required'
},
{
id: 4,
name: 'Advanced Analytics',
disabled: true,
disabledReason: 'Upgrade required'
}
]
}
]
}
}
}
</script>
<style>
.feature-option.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.disabled-reason {
font-size: 11px;
color: #999;
font-style: italic;
}
</style>Install with Tessl CLI
npx tessl i tessl/npm-vue-multiselect