Skip to main content

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>

With Search Input

<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>

With Clear Button

<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>

With Form

<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

modelValue
any
The controlled value. Can be bound with v-model.
defaultValue
any
The default value when initially rendered.
items
array
Array of items or nested arrays for grouped items.
valueKey
string
Field to use as the value when items are objects.
labelKey
string
default:"label"
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
string
Placeholder text when no item is selected.
searchInput
boolean | object
default:"true"
Display the search input. Can be an object to pass additional props.
multiple
boolean
Allow multiple items to be selected.
createItem
boolean | 'always' | object
Allow creating custom items.
filterFields
string[]
Fields to filter items by.
ignoreFilter
boolean
Disable default filtering.
virtualize
boolean | object
Enable virtualization for large lists.
searchTerm
string
The current search term. Can be bound with v-model:search-term.
clear
boolean | object
Show a clear button.
clearIcon
string
Icon for the clear button.
selectedIcon
string
Icon shown for selected items.
trailingIcon
string
Icon displayed at the end.
color
string
default:"primary"
Color variant.
variant
string
default:"outline"
Visual variant.
size
string
default:"md"
Size variant.
disabled
boolean
Disable the select.
portal
boolean | string | HTMLElement
default:"true"
Render the menu in a portal.
content
object
Content props (side, sideOffset, etc.).
arrow
boolean | object
Display an arrow alongside the menu.

Events

@update:modelValue
(value: any) => void
Emitted when the selected value changes.
@update:searchTerm
(value: string) => void
Emitted when the search term changes.
@create
(item: string) => void
Emitted when a new item is created.
@clear
() => void
Emitted when the clear button is clicked.
@change
(event: Event) => void
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.
empty
{ searchTerm: string }
Custom empty state.
create-item-label
{ item: string }
Custom create item label.

Exposed

triggerRef
Ref<HTMLButtonElement>
Reference to the trigger button.
viewportRef
Ref<HTMLElement>
Reference to the viewport element.

Build docs developers (and LLMs) love