forms
Display a select menu with advanced features.
The SelectMenu component renders by default a 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, variant, size, placeholder, icon, disabled, etc.
Like the Select component, you can use the options
prop to pass an array of strings or objects.
<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>
You can use the multiple
prop to select multiple values.
<script setup>
const people = [...]
const selected = ref([])
</script>
<template>
<USelectMenu v-model="selected" :options="people" multiple placeholder="Select people" />
</template>
You can override the #label
slot and handle the display yourself.
<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>
You can also override the #default
slot entirely.
<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>
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
.
<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>
Use any icon from Iconify by setting the icon
prop by using this pattern: i-{collection_name}-{icon_name}
.
Use the trailing-icon
prop to set a different icon or change it globally in ui.select.default.trailingIcon
. Defaults to i-heroicons-chevron-down-20-solid
.
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
.
<USelectMenu icon="i-heroicons-magnifying-glass-20-solid" color="white" />
Use the searchable
prop to enable search.
Use the searchable-placeholder
prop to set a different placeholder.
This will use Headless UI Combobox component instead of Listbox.
<USelectMenu searchable searchable-placeholder="Search a person..." />
Prop | Default | Description |
---|---|---|
modelValue | "" | string | number | Record<string, any> | unknown[] |
options | [] | string[] | { [key: string]: any; disabled?: boolean; }[] |
searchAttributes | null | unknown[] |
popper | {} | {} |
name | null | string |
placeholder | null | string |
icon | null | string |
trailingIcon | appConfig.ui.select.default.trailingIcon | string |
size | appConfig.ui.select.default.size | string |
color | appConfig.ui.select.default.color | string |
variant | appConfig.ui.select.default.variant | string |
by | undefined | string |
selectedIcon | appConfig.ui.selectMenu.default.selectedIcon | string |
searchablePlaceholder | "Search..." | string |
optionAttribute | "label" | string |
required | false | boolean |
disabled | false | boolean |
padded | true | boolean |
multiple | false | boolean |
searchable | false | boolean |
creatable | false | boolean |
ui | appConfig.ui.selectMenu | any |
uiSelect | appConfig.ui.select | any |
{
"wrapper": "relative inline-flex",
"container": "z-20",
"width": "w-full",
"height": "max-h-60",
"base": "relative focus:outline-none overflow-y-auto scroll-py-1",
"background": "bg-white dark:bg-gray-800",
"shadow": "shadow-lg",
"rounded": "rounded-md",
"padding": "p-1",
"ring": "ring-1 ring-gray-200 dark:ring-gray-700",
"input": "block w-[calc(100%+0.5rem)] focus:ring-transparent text-sm px-3 py-1.5 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border-0 border-b border-gray-200 dark:border-gray-700 focus:border-inherit sticky -top-1 -mt-1 mb-1 -mx-1 z-10 placeholder-gray-400 dark:placeholder-gray-500",
"option": {
"base": "cursor-default select-none relative flex items-center justify-between gap-1",
"rounded": "rounded-md",
"padding": "px-2 py-1.5",
"size": "text-sm",
"color": "text-gray-900 dark:text-white",
"container": "flex items-center gap-2 min-w-0",
"active": "bg-gray-100 dark:bg-gray-900",
"inactive": "",
"selected": "pr-7",
"disabled": "cursor-not-allowed opacity-50",
"empty": "text-sm text-gray-400 dark:text-gray-500 px-2 py-1.5",
"icon": {
"base": "flex-shrink-0 h-4 w-4",
"active": "text-gray-900 dark:text-white",
"inactive": "text-gray-400 dark:text-gray-500"
},
"selectedIcon": {
"wrapper": "absolute inset-y-0 right-0 flex items-center",
"padding": "pr-2",
"base": "h-4 w-4 text-gray-900 dark:text-white flex-shrink-0"
},
"avatar": {
"base": "flex-shrink-0",
"size": "3xs"
},
"chip": {
"base": "flex-shrink-0 w-2 h-2 mx-1 rounded-full"
}
},
"transition": {
"leaveActiveClass": "transition ease-in duration-100",
"leaveFromClass": "opacity-100",
"leaveToClass": "opacity-0"
},
"popper": {
"placement": "bottom-end"
},
"default": {
"selectedIcon": "i-heroicons-check-20-solid"
}
}