Skip to main content

Overview

The InputMenu component combines a text input with a dropdown menu, providing searchable autocomplete functionality, custom filtering, multiple selection, and the ability to create new items.

Basic Usage

<script setup lang="ts">
import { ref } from 'vue'

const selected = ref(null)
const items = [
  'Apple',
  'Banana',
  'Cherry',
  'Date',
  'Elderberry'
]
</script>

<template>
  <UInputMenu 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>
  <UInputMenu
    v-model="selected"
    :items="items"
    value-key="id"
    placeholder="Select a person"
  />
</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>
  <UInputMenu v-model="selected" :items="items" />
</template>

Multiple Selection

<script setup lang="ts">
import { ref } from 'vue'

const selected = ref([])
const items = ['React', 'Vue', 'Angular', 'Svelte', 'Solid']
</script>

<template>
  <UInputMenu
    v-model="selected"
    :items="items"
    multiple
    placeholder="Select frameworks"
  />
</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>
  <UInputMenu
    v-model="selected"
    :items="items"
    create-item
    @create="onCreate"
    placeholder="Type to create..."
  />
</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>
  <UInputMenu v-model="selected" :items="items" />
</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>
  <UInputMenu
    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>
  <UInputMenu
    v-model="selected"
    :items="items"
    virtualize
    placeholder="Select from 10k items"
  />
</template>

With Clear Button

<template>
  <UInputMenu
    v-model="selected"
    :items="items"
    clear
    placeholder="Select an item"
    @clear="console.log('Cleared')"
  />
</template>

Custom Slots

<template>
  <UInputMenu 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>
  </UInputMenu>
</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 the input is empty.
multiple
boolean
Allow multiple items to be selected.
createItem
boolean | 'always' | object
Allow creating custom items. Can be configured with position and when options.
filterFields
string[]
Fields to filter items by. Defaults to [labelKey].
ignoreFilter
boolean
Disable default filtering for custom 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 to reset the value.
clearIcon
string
Icon for the clear button.
selectedIcon
string
Icon shown for selected items.
deleteIcon
string
Icon for delete button in tags (multiple mode).
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 input.
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 the input loses focus.
@focus
(event: FocusEvent) => void
Emitted when the input gains focus.

Slots

leading
{ modelValue: any, open: boolean, ui: object }
Custom leading 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.

Build docs developers (and LLMs) love