Overview
The SelectMenu component combines a select dropdown with search functionality, providing filtering, custom item creation, multiple selection, and virtualization for large lists.
Basic Usage
<script setup lang="ts">
import { ref } from 'vue'
const selected = ref(null)
const items = [
'Apple',
'Banana',
'Cherry',
'Date',
'Elderberry'
]
</script>
<template>
<USelectMenu v-model="selected" :items="items" placeholder="Select a fruit" />
</template>
With Objects
<script setup lang="ts">
import { ref } from 'vue'
const selected = ref(null)
const items = [
{ id: 1, label: 'Wade Cooper', description: 'Software Engineer' },
{ id: 2, label: 'Arlene Mccoy', description: 'Product Manager' },
{ id: 3, label: 'Devon Webb', description: 'Designer' }
]
</script>
<template>
<USelectMenu
v-model="selected"
:items="items"
value-key="id"
placeholder="Select a person"
/>
</template>
<template>
<!-- Default search -->
<USelectMenu v-model="selected" :items="items" search-input />
<!-- Custom search placeholder -->
<USelectMenu
v-model="selected"
:items="items"
:search-input="{ placeholder: 'Search people...' }"
/>
<!-- No search -->
<USelectMenu v-model="selected" :items="items" :search-input="false" />
</template>
Multiple Selection
<script setup lang="ts">
import { ref } from 'vue'
const selected = ref([])
const items = ['React', 'Vue', 'Angular', 'Svelte', 'Solid']
</script>
<template>
<USelectMenu
v-model="selected"
:items="items"
multiple
placeholder="Select frameworks"
/>
</template>
Grouped Items
<script setup lang="ts">
const items = [
[
{ type: 'label', label: 'Fruits' },
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' }
],
[
{ type: 'label', label: 'Vegetables' },
{ label: 'Carrot', value: 'carrot' },
{ label: 'Potato', value: 'potato' }
]
]
</script>
<template>
<USelectMenu v-model="selected" :items="items" />
</template>
Create Custom Items
<script setup lang="ts">
import { ref } from 'vue'
const selected = ref(null)
const items = ref(['Apple', 'Banana', 'Cherry'])
function onCreate(item: string) {
items.value.push(item)
selected.value = item
}
</script>
<template>
<USelectMenu
v-model="selected"
:items="items"
create-item
@create="onCreate"
placeholder="Type to create..."
/>
</template>
Custom Filtering
<script setup lang="ts">
import { ref } from 'vue'
const selected = ref(null)
const searchTerm = ref('')
const items = ref([])
// Fetch items from API based on search
watchDebounced(searchTerm, async (value) => {
if (!value) return
const { data } = await useFetch('/api/search', {
query: { q: value }
})
items.value = data.value
}, { debounce: 300 })
</script>
<template>
<USelectMenu
v-model="selected"
v-model:search-term="searchTerm"
:items="items"
ignore-filter
placeholder="Search..."
/>
</template>
Virtualization
Efficiently render large lists:
<script setup lang="ts">
const items = Array.from({ length: 10000 }, (_, i) => ({
label: `Item ${i + 1}`,
value: i
}))
</script>
<template>
<USelectMenu
v-model="selected"
:items="items"
virtualize
placeholder="Select from 10k items"
/>
</template>
<template>
<USelectMenu
v-model="selected"
:items="items"
clear
placeholder="Select an item"
@clear="console.log('Cleared')"
/>
</template>
With Icons and Avatars
<script setup lang="ts">
const items = [
{
label: 'Profile',
icon: 'i-heroicons-user',
value: 'profile'
},
{
label: 'Settings',
icon: 'i-heroicons-cog',
value: 'settings'
},
{
label: 'Team',
avatar: { src: 'https://i.pravatar.cc/150?img=1' },
value: 'team'
}
]
</script>
<template>
<USelectMenu v-model="selected" :items="items" />
</template>
Colors and Variants
<template>
<!-- Colors -->
<USelectMenu :items="items" color="primary" />
<USelectMenu :items="items" color="secondary" />
<USelectMenu :items="items" color="success" />
<!-- Variants -->
<USelectMenu :items="items" variant="outline" />
<USelectMenu :items="items" variant="soft" />
<USelectMenu :items="items" variant="ghost" />
</template>
Custom Slots
<template>
<USelectMenu v-model="selected" :items="items">
<template #item="{ item }">
<div class="flex items-center justify-between w-full">
<span>{{ item.label }}</span>
<UBadge>{{ item.count }}</UBadge>
</div>
</template>
<template #empty="{ searchTerm }">
<p>No results for "{{ searchTerm }}"</p>
</template>
</USelectMenu>
</template>
<script setup lang="ts">
import { z } from 'zod'
import { ref } from 'vue'
const schema = z.object({
country: z.string().min(1, 'Please select a country'),
skills: z.array(z.string()).min(1, 'Select at least one skill')
})
const state = ref({
country: '',
skills: []
})
const countries = [
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'mx', label: 'Mexico' }
]
const skills = [
'JavaScript', 'TypeScript', 'Vue', 'React', 'Node.js'
]
</script>
<template>
<UForm :schema="schema" :state="state">
<UFormField label="Country" name="country">
<USelectMenu v-model="state.country" :items="countries" />
</UFormField>
<UFormField label="Skills" name="skills">
<USelectMenu v-model="state.skills" :items="skills" multiple />
</UFormField>
</UForm>
</template>
Props
The controlled value. Can be bound with v-model.
The default value when initially rendered.
Array of items or nested arrays for grouped items.
Field to use as the value when items are objects.
Field to use as the label when items are objects.
descriptionKey
string
default:"description"
Field to use as the description when items are objects.
Placeholder text when no item is selected.
searchInput
boolean | object
default:"true"
Display the search input. Can be an object to pass additional props.
Allow multiple items to be selected.
createItem
boolean | 'always' | object
Allow creating custom items.
Fields to filter items by.
Disable default filtering.
Enable virtualization for large lists.
The current search term. Can be bound with v-model:search-term.
Icon for the clear button.
Icon shown for selected items.
Icon displayed at the end.
portal
boolean | string | HTMLElement
default:"true"
Render the menu in a portal.
Content props (side, sideOffset, etc.).
Display an arrow alongside the menu.
Events
Emitted when the selected value changes.
Emitted when the search term changes.
Emitted when a new item is created.
Emitted when the clear button is clicked.
Emitted when the value is committed.
@blur
(event: FocusEvent) => void
Emitted when focus is lost.
@focus
(event: FocusEvent) => void
Emitted when focused.
Slots
leading
{ modelValue: any, open: boolean, ui: object }
Custom leading content.
default
{ modelValue: any, open: boolean, ui: object }
Custom trigger content.
trailing
{ modelValue: any, open: boolean, ui: object }
Custom trailing content.
item
{ item: any, index: number, ui: object }
Custom item rendering.
item-leading
{ item: any, index: number, ui: object }
Custom item leading content.
item-trailing
{ item: any, index: number, ui: object }
Custom item trailing content.
Custom create item label.
Exposed
Reference to the trigger button.
Reference to the viewport element.