---
title: SelectMenu
description: Display a select menu with advanced features.
links:
  - label: GitHub
    icon: i-simple-icons-github
    to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
  - label: 'Listbox'
    icon: i-simple-icons-headlessui
    to: 'https://headlessui.com/vue/listbox'
---

## Usage

The `SelectMenu` component renders by default a [Select](/forms/select) component and is based on the `ui.select` preset. You can use most of the `Select` props to configure the display if you don't want to override the default slot such as [color](/forms/select#style), [variant](/forms/select#style), [size](/forms/select#size), [placeholder](/forms/select#placeholder), [icon](/forms/select#icon), [disabled](/forms/select#disabled), etc.

Like the `Select` component, you can use the `options` prop to pass an array of strings or objects.

::component-example
#default
:select-menu-example-basic{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']

const selected = ref(people[0])
</script>

<template>
  <USelectMenu v-model="selected" :options="people" />
</template>
```
::

### Multiple

You can use the `multiple` prop to select multiple values.

::component-example
#default
:select-menu-example-multiple{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = [...]

const selected = ref([])
</script>

<template>
  <USelectMenu v-model="selected" :options="people" multiple placeholder="Select people" />
</template>
```
::

### Objects

You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.

::component-example
#default
:select-menu-example-objects{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [{
  id: 'benjamincanac',
  label: 'benjamincanac',
  href: 'https://github.com/benjamincanac',
  target: '_blank',
  avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' }
},
{
  id: 'Atinux',
  label: 'Atinux',
  href: 'https://github.com/Atinux',
  target: '_blank',
  avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' }
},
{
  id: 'smarroufin',
  label: 'smarroufin',
  href: 'https://github.com/smarroufin',
  target: '_blank',
  avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' }
},
{
  id: 'nobody',
  label: 'Nobody',
  icon: 'i-heroicons-user-circle'
}]

const selected = ref(people[0])
</script>

<template>
  <USelectMenu v-model="selected" :options="people">
    <template #label>
      <UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4" />
      <UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" />

      {{ selected.label }}
    </template>
  </USelectMenu>
</template>
```
::

If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.

::component-example
#default
:select-menu-example-objects-value-attribute{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = [{
  id: 1,
  name: 'Wade Cooper'
}, {
  id: 2,
  name: 'Arlene Mccoy'
}, {
  id: 3,
  name: 'Devon Webb'
}, {
  id: 4,
  name: 'Tom Cook'
}]

const selected = ref(people[0].id)

const current = computed(() => people.find(person => person.id === selected.value))
</script>

<template>
  <USelectMenu
    v-model="selected"
    :options="people"
    placeholder="Select people"
    value-attribute="id"
    option-attribute="name"
  >
    <template #label>
      {{ current.name }}
    </template>
  </USelectMenu>
</template>
```
::

### Icon

Use the `selected-icon` prop to set a different icon or change it globally in `ui.selectMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.

::component-card
---
baseProps:
  class: 'w-full lg:w-40'
  placeholder: 'Select a person'
  options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
props:
  selectedIcon: 'i-heroicons-hand-thumb-up-solid'
excludedProps:
  - selectedIcon
---
::

::callout{icon="i-heroicons-light-bulb"}
Learn how to customize icons from the [Select](/forms/select#icon) component.
::

### Search

Use the `searchable` prop to enable search.

Use the `searchable-placeholder` prop to set a different placeholder.

This will use Headless UI [Combobox](https://headlessui.com/vue/combobox) component instead of [Listbox](https://headlessui.com/vue/listbox).

::component-card
---
baseProps:
  class: 'w-full lg:w-40'
  placeholder: 'Select a person'
  options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
props:
  searchable: true
  searchablePlaceholder: 'Search a person...'
---
::

### Async search

Pass a function to the `searchable` prop to customize the search behavior and filter options according to your needs. The function will receive the query as its first argument and should return an array.

Use the `debounce` prop to adjust the delay of the function.

::component-example
#default
:select-menu-example-async-search{class="w-full lg:w-40"}

#code
```vue
<script setup>
const search = async (q) => {
  const users = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })

  return users.map(user => ({ id: user.id, label: user.name, suffix: user.email })).filter(Boolean)
}

const selected = ref([])
</script>

<template>
  <USelectMenu
    v-model="selected"
    :searchable="search"
    placeholder="Search for a user..."
    multiple
    by="id"
  />
</template>
```
::

### Create option

Use the `creatable` prop to enable the creation of new options when the search doesn't return any results (only works with `searchable`).

Try to search for something that doesn't exist in the example below.

::component-example
#default
:select-menu-example-creatable{class="w-full lg:w-40"}

#code
```vue
<script setup>
const options = ref([
  { id: 1, name: 'bug', color: 'd73a4a' },
  { id: 2, name: 'documentation', color: '0075ca' },
  { id: 3, name: 'duplicate', color: 'cfd3d7' },
  { id: 4, name: 'enhancement', color: 'a2eeef' },
  { id: 5, name: 'good first issue', color: '7057ff' },
  { id: 6, name: 'help wanted', color: '008672' },
  { id: 7, name: 'invalid', color: 'e4e669' },
  { id: 8, name: 'question', color: 'd876e3' },
  { id: 9, name: 'wontfix', color: 'ffffff' }
])

const selected = ref([])

const labels = computed({
  get: () => selected.value,
  set: async (labels) => {
    const promises = labels.map(async (label) => {
      if (label.id) {
        return label
      }

      // In a real app, you would make an API call to create the label
      const response = {
        name: label.name,
        color: generateColorFromString(label.name)
      }

      options.value.push(response)

      return response
    })

    selected.value = await Promise.all(promises)
  }
})

// Look at the component example to see how this is used
function generateColorFromString (str) {
  // ...
}
</script>

<template>
  <USelectMenu
    v-model="labels"
    by="id"
    name="labels"
    :options="options"
    option-attribute="name"
    multiple
    searchable
    creatable
  >
    <template #label>
      <template v-if="labels.length">
        <span class="flex items-center -space-x-1">
          <span v-for="label of labels" :key="label.id" class="flex-shrink-0 w-2 h-2 mt-px rounded-full" :style="{ background: `#${label.color}` }" />
        </span>
        <span>{{ labels.length }} label{{ labels.length > 1 ? 's' : '' }}</span>
      </template>
      <template v-else>
        <span class="text-gray-500 dark:text-gray-400 truncate">Select labels</span>
      </template>
    </template>

    <template #option="{ option }">
      <span
        class="flex-shrink-0 w-2 h-2 mt-px rounded-full"
        :style="{ background: `#${option.color}` }"
      />
      <span class="truncate">{{ option.name }}</span>
    </template>

    <template #option-create="{ option }">
      <span class="flex-shrink-0">New label:</span>
      <span
        class="flex-shrink-0 w-2 h-2 mt-px rounded-full -mx-1"
        :style="{ background: `#${generateColorFromString(option.name)}` }"
      />
      <span class="block truncate">{{ option.name }}</span>
    </template>
  </USelectMenu>
</template>
```
::

## Slots

### `label`

You can override the `#label` slot and handle the display yourself.

::component-example
#default
:select-menu-example-multiple-slot{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = [...]

const selected = ref([])
</script>

<template>
  <USelectMenu v-model="selected" :options="people" multiple>
    <template #label>
      <span v-if="selected.length" class="truncate">{{ selected.join(', ') }}</span>
      <span v-else>Select people</span>
    </template>
  </USelectMenu>
</template>
```
::

### `default`

You can also override the `#default` slot entirely.

::component-example
#default
:select-menu-example-button{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = [...]

const selected = ref(people[3])
</script>

<template>
  <USelectMenu v-slot="{ open }" v-model="selected" :options="people">
    <UButton color="gray">
      {{ selected }}

      <UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform" :class="[open && 'transform rotate-90']" />
    </UButton>
  </USelectMenu>
</template>
```
::

### `option`

Use the `#option` slot to customize the option content. You will have access to the `option`, `active` and `selected` properties in the slot scope.

::component-example
#default
:select-menu-example-option-slot{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = [
  { name: 'Wade Cooper', online: true },
  { name: 'Arlene Mccoy', online: false },
  { name: 'Devon Webb', online: false },
  { name: 'Tom Cook', online: true },
  { name: 'Tanya Fox', online: false },
  { name: 'Hellen Schmidt', online: true },
  { name: 'Caroline Schultz', online: true },
  { name: 'Mason Heaney', online: false },
  { name: 'Claudie Smitham', online: true },
  { name: 'Emil Schaefer', online: false }
]

const selected = ref(people[3])
</script>

<template>
  <USelectMenu v-model="selected" :options="people" option-attribute="name">
    <template #label>
      <span :class="[selected.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
      <span class="truncate">{{ selected.name }}</span>
    </template>

    <template #option="{ option: person }">
      <span :class="[person.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
      <span class="truncate">{{ person.name }}</span>
    </template>
  </USelectMenu>
</template>
```
::

### `option-empty`

Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.

::component-example
#default
:select-menu-example-option-empty-slot{class="w-full lg:w-40"}

#code
```vue
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']

const selected = ref(people[0])
</script>

<template>
  <USelectMenu v-model="selected" :options="people" searchable>
    <template #option-empty="{ query }">
      <q>{{ query }}</q> not found
    </template>
  </USelectMenu>
</template>
```
::

### `option-create`

Use the `#option-create` slot to customize the content displayed when the `creatable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.

::callout{icon="i-heroicons-light-bulb"}
An example is available in the [Create option](#create-option) section.
::

## Props

:component-props

## Preset

:component-preset
