Autocomplete
Autocomplete lets users choose one option from a searchable list.
Import
vue
<script setup lang="ts">
import { Autocomplete } from '@heroui-vue/vue'
</script>Usage
Basic
Select an animal
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedAnimal"
label="Favorite Animal"
placeholder="Select an animal"
search-placeholder="Search animals..."
:items="animals"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedAnimal = ref<string | number | null>(null)
const animals = [
{ id: 'cat', label: 'Cat' },
{ id: 'dog', label: 'Dog' },
{ id: 'elephant', label: 'Elephant' },
{ id: 'lion', label: 'Lion' },
{ id: 'tiger', label: 'Tiger' },
{ id: 'giraffe', label: 'Giraffe' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>Anatomy
vue
<template>
<Autocomplete
v-model="selectedKey"
label="State"
placeholder="Select one"
search-placeholder="Search states..."
:items="items"
/>
</template>With Description
Select one
Select your state of residence
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedState"
description="Select your state of residence"
label="State"
placeholder="Select one"
search-placeholder="Search states..."
:items="states"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedState = ref<string | number | null>(null)
const states = [
{ id: 'florida', label: 'Florida' },
{ id: 'delaware', label: 'Delaware' },
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'new-york', label: 'New York' },
{ id: 'washington', label: 'Washington' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>Multiple Select
Select states
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedStates"
label="States"
placeholder="Select states"
search-placeholder="Search states..."
selection-mode="multiple"
:items="states"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedStates = ref<(string | number)[]>([])
const states = [
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'florida', label: 'Florida' },
{ id: 'new-york', label: 'New York' },
{ id: 'illinois', label: 'Illinois' },
{ id: 'pennsylvania', label: 'Pennsylvania' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>With Sections
Select a country
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedCountry"
label="Country"
placeholder="Select a country"
search-placeholder="Search countries..."
:items="countries"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedCountry = ref<string | number | null>(null)
const countries = [
{ id: 'usa', label: 'United States', section: 'North America' },
{ id: 'canada', label: 'Canada', section: 'North America' },
{ id: 'mexico', label: 'Mexico', section: 'North America' },
{ id: 'uk', label: 'United Kingdom', section: 'Europe' },
{ id: 'france', label: 'France', section: 'Europe' },
{ id: 'germany', label: 'Germany', section: 'Europe' },
{ id: 'spain', label: 'Spain', section: 'Europe' },
{ id: 'italy', label: 'Italy', section: 'Europe' },
{ id: 'japan', label: 'Japan', section: 'Asia' },
{ id: 'china', label: 'China', section: 'Asia' },
{ id: 'india', label: 'India', section: 'Asia' },
{ id: 'south-korea', label: 'South Korea', section: 'Asia' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>With Disabled Options
Select an animal
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedAnimal"
label="Animal"
placeholder="Select an animal"
search-placeholder="Search animals..."
:disabled-keys="['cat', 'kangaroo']"
:items="animals"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedAnimal = ref<string | number | null>(null)
const animals = [
{ id: 'dog', label: 'Dog' },
{ id: 'cat', label: 'Cat' },
{ id: 'bird', label: 'Bird' },
{ id: 'kangaroo', label: 'Kangaroo' },
{ id: 'elephant', label: 'Elephant' },
{ id: 'tiger', label: 'Tiger' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>Allows Empty Collection
Select one
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedState"
label="State"
placeholder="Select one"
search-placeholder="Search states..."
empty-text="No results found"
:items="states"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedState = ref<string | number | null>(null)
const states: Array<{ id: string; label: string }> = []
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>Custom Indicator
Select an animal
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedAnimal"
label="Animal"
placeholder="Select an animal"
search-placeholder="Search animals..."
:items="animals"
>
<template #indicator="{ className, isOpen }">
<svg
:class="[className, 'demo-autocomplete-indicator']"
:data-open="isOpen"
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
aria-hidden="true"
>
<path d="M8 3v10M3 8h10" />
</svg>
</template>
</Autocomplete>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedAnimal = ref<string | number | null>(null)
const animals = [
{ id: 'cat', label: 'Cat' },
{ id: 'dog', label: 'Dog' },
{ id: 'elephant', label: 'Elephant' },
{ id: 'lion', label: 'Lion' },
{ id: 'tiger', label: 'Tiger' },
]
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
.demo-autocomplete-indicator {
width: 0.75rem;
height: 0.75rem;
}
</style>Required
<template>
<form class="demo-autocomplete-required" @submit.prevent="handleSubmit">
<Autocomplete
v-model="selectedState"
full-width
label="State"
name="state"
placeholder="Select one"
required
search-placeholder="Search states..."
:items="states"
/>
<Autocomplete
v-model="selectedCountry"
full-width
label="Country"
name="country"
placeholder="Select a country"
required
search-placeholder="Search countries..."
:items="countries"
/>
<Button type="submit">Submit</Button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete, Button } from '@heroui-vue/vue'
const selectedState = ref<string | number | null>(null)
const selectedCountry = ref<string | number | null>(null)
const states = [
{ id: 'florida', label: 'Florida' },
{ id: 'delaware', label: 'Delaware' },
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'new-york', label: 'New York' },
{ id: 'washington', label: 'Washington' },
]
const countries = [
{ id: 'usa', label: 'United States' },
{ id: 'canada', label: 'Canada' },
{ id: 'mexico', label: 'Mexico' },
{ id: 'uk', label: 'United Kingdom' },
{ id: 'france', label: 'France' },
{ id: 'germany', label: 'Germany' },
]
const handleSubmit = () => {
window.alert('Form submitted successfully!')
}
</script>
<style lang="less">
.demo-autocomplete-required {
display: flex;
width: 16rem;
flex-direction: column;
align-items: stretch;
gap: 1rem;
}
</style>Full Width
Select one
<template>
<Surface class="demo-autocomplete-surface">
<Autocomplete
v-model="selectedState"
full-width
label="State"
placeholder="Select one"
search-placeholder="Search states..."
variant="secondary"
:items="states"
/>
</Surface>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete, Surface } from '@heroui-vue/vue'
const selectedState = ref<string | number | null>(null)
const states = [
{ id: 'florida', label: 'Florida' },
{ id: 'delaware', label: 'Delaware' },
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'new-york', label: 'New York' },
{ id: 'washington', label: 'Washington' },
]
</script>
<style lang="less">
.demo-autocomplete-surface {
width: 23.75rem;
max-width: 100%;
padding: 1.5rem;
border-radius: 1.5rem;
}
</style>Variants
Single Select Variants
Select one
Select one
Multiple Select Variants
Select multiple
Select multiple
<template>
<div class="demo-autocomplete-variants">
<div class="demo-autocomplete-variant-section">
<h3>Single Select Variants</h3>
<Autocomplete
v-model="selectedPrimary"
label="Primary variant"
placeholder="Select one"
search-placeholder="Search..."
:items="items"
/>
<Autocomplete
v-model="selectedSecondary"
label="Secondary variant"
placeholder="Select one"
search-placeholder="Search..."
variant="secondary"
:items="items"
/>
</div>
<div class="demo-autocomplete-variant-section">
<h3>Multiple Select Variants</h3>
<Autocomplete
v-model="selectedPrimaryMultiple"
label="Primary variant"
placeholder="Select multiple"
search-placeholder="Search..."
selection-mode="multiple"
:items="items"
/>
<Autocomplete
v-model="selectedSecondaryMultiple"
label="Secondary variant"
placeholder="Select multiple"
search-placeholder="Search..."
selection-mode="multiple"
variant="secondary"
:items="items"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedPrimary = ref<string | number | null>(null)
const selectedSecondary = ref<string | number | null>(null)
const selectedPrimaryMultiple = ref<(string | number)[]>([])
const selectedSecondaryMultiple = ref<(string | number)[]>([])
const items = [
{ id: 'option1', label: 'Option 1' },
{ id: 'option2', label: 'Option 2' },
{ id: 'option3', label: 'Option 3' },
{ id: 'option4', label: 'Option 4' },
]
</script>
<style lang="less">
.demo-autocomplete-variants {
display: flex;
flex-direction: column;
gap: 2rem;
}
.demo-autocomplete-variant-section {
display: flex;
width: 16rem;
flex-direction: column;
gap: 1rem;
}
.demo-autocomplete-variant-section h3 {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
}
</style>In Surface
Select framework
<template>
<Surface class="demo-autocomplete-in-surface">
<Autocomplete
v-model="selectedFramework"
full-width
label="Framework"
placeholder="Select framework"
search-placeholder="Search frameworks..."
variant="secondary"
:items="frameworks"
/>
</Surface>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete, Surface } from '@heroui-vue/vue'
const selectedFramework = ref<string | number | null>(null)
const frameworks = [
{ id: 'vue', label: 'Vue' },
{ id: 'react', label: 'React' },
{ id: 'svelte', label: 'Svelte' },
{ id: 'solid', label: 'Solid' },
{ id: 'angular', label: 'Angular' },
]
</script>
<style lang="less">
.demo-autocomplete-in-surface {
width: 23.75rem;
max-width: 100%;
padding: 1.5rem;
border-radius: 1.5rem;
text-align: left;
}
</style>Custom Value
Select a user
<template>
<Autocomplete
v-model="selectedUser"
class="demo-autocomplete-users"
label="User"
placeholder="Select a user"
search-placeholder="Search users..."
:items="users"
>
<template #value="{ item, label }">
<span v-if="item" class="demo-autocomplete-users__value">
<span
class="demo-autocomplete-users__avatar demo-autocomplete-users__avatar--small"
:style="{ background: String(item.color) }"
aria-hidden="true"
>
{{ item.fallback }}
</span>
<span>{{ label }}</span>
</span>
</template>
<template #item="{ item, selected }">
<span
class="demo-autocomplete-users__avatar"
:style="{ background: String(item.color) }"
aria-hidden="true"
>
{{ item.fallback }}
</span>
<span class="demo-autocomplete-users__details">
<Label>{{ item.label }}</Label>
<Description>{{ item.description }}</Description>
</span>
<span
v-if="selected"
class="list-box-item__indicator"
data-slot="list-box-item-indicator"
aria-hidden="true"
>
<svg
viewBox="0 0 16 16"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
data-slot="list-box-item-indicator--checkmark"
>
<path d="M3.5 8.5L6.5 11.5L12.5 4.5" />
</svg>
</span>
</template>
</Autocomplete>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete, Description, Label } from '@heroui-vue/vue'
const selectedUser = ref<string | number | null>(null)
const users = [
{
color: '#3b82f6',
description: 'bob@heroui.com',
fallback: 'B',
id: '1',
label: 'Bob',
},
{
color: '#22c55e',
description: 'fred@heroui.com',
fallback: 'F',
id: '2',
label: 'Fred',
},
{
color: '#a855f7',
description: 'martha@heroui.com',
fallback: 'M',
id: '3',
label: 'Martha',
},
{
color: '#ef4444',
description: 'john@heroui.com',
fallback: 'J',
id: '4',
label: 'John',
},
{
color: '#f97316',
description: 'jane@heroui.com',
fallback: 'J',
id: '5',
label: 'Jane',
},
]
</script>
<style lang="less">
.demo-autocomplete-users {
width: 16rem;
}
.demo-autocomplete-users__value,
.demo-autocomplete-users__details {
display: flex;
min-width: 0;
}
.demo-autocomplete-users__value {
align-items: center;
gap: 0.5rem;
}
.demo-autocomplete-users__details {
flex: 1;
flex-direction: column;
align-items: flex-start;
text-align: left;
}
.demo-autocomplete-users__avatar {
display: inline-flex;
width: 2rem;
height: 2rem;
flex-shrink: 0;
align-items: center;
justify-content: center;
border-radius: 999px;
color: #fff;
font-size: 0.8125rem;
font-weight: 600;
}
.demo-autocomplete-users__avatar--small {
width: 1rem;
height: 1rem;
font-size: 0.625rem;
}
</style>Controlled
California
Selected: California
<template>
<div class="demo-autocomplete-with-state">
<Autocomplete
v-model="selectedState"
label="State (controlled)"
placeholder="Select a state"
search-placeholder="Search states..."
:items="states"
/>
<p class="demo-autocomplete-note">
Selected: {{ selectedStateLabel }}
</p>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedState = ref<string | number | null>('california')
const states = [
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'florida', label: 'Florida' },
{ id: 'new-york', label: 'New York' },
{ id: 'illinois', label: 'Illinois' },
{ id: 'pennsylvania', label: 'Pennsylvania' },
]
const selectedStateLabel = computed(
() => states.find((state) => state.id === selectedState.value)?.label ?? 'None',
)
</script>
<style lang="less">
.demo-autocomplete-with-state {
display: flex;
width: 16rem;
flex-direction: column;
gap: 0.5rem;
}
.demo-autocomplete-note {
margin: 0;
color: var(--color-muted);
font-size: 0.875rem;
}
</style>Controlled Multiple
Selected: California, Texas
<template>
<div class="demo-autocomplete-with-state">
<Autocomplete
v-model="selectedStates"
label="States (controlled)"
placeholder="Select states"
search-placeholder="Search states..."
selection-mode="multiple"
:items="states"
/>
<p class="demo-autocomplete-note">
Selected: {{ selectedStateLabels }}
</p>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedStates = ref<(string | number)[]>(['california', 'texas'])
const states = [
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'florida', label: 'Florida' },
{ id: 'new-york', label: 'New York' },
{ id: 'illinois', label: 'Illinois' },
{ id: 'pennsylvania', label: 'Pennsylvania' },
]
const selectedStateLabels = computed(() => {
const labels = selectedStates.value
.map((key) => states.find((state) => state.id === key)?.label)
.filter(Boolean)
return labels.length ? labels.join(', ') : 'None'
})
</script>
<style lang="less">
.demo-autocomplete-with-state {
display: flex;
width: 16rem;
flex-direction: column;
gap: 0.5rem;
}
.demo-autocomplete-note {
margin: 0;
color: var(--color-muted);
font-size: 0.875rem;
}
</style>Controlled Open State
Select one
Autocomplete is closed
<template>
<div class="demo-autocomplete-with-state">
<Autocomplete
v-model="selectedState"
v-model:is-open="isOpen"
label="State"
placeholder="Select one"
search-placeholder="Search states..."
:items="states"
/>
<Button @click="isOpen = !isOpen">
{{ isOpen ? 'Close' : 'Open' }} Autocomplete
</Button>
<p class="demo-autocomplete-note">
Autocomplete is {{ isOpen ? 'open' : 'closed' }}
</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete, Button } from '@heroui-vue/vue'
const isOpen = ref(false)
const selectedState = ref<string | number | null>(null)
const states = [
{ id: 'florida', label: 'Florida' },
{ id: 'delaware', label: 'Delaware' },
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'new-york', label: 'New York' },
{ id: 'washington', label: 'Washington' },
]
</script>
<style lang="less">
.demo-autocomplete-with-state {
display: flex;
width: 16rem;
flex-direction: column;
gap: 1rem;
}
.demo-autocomplete-note {
margin: 0;
color: var(--color-muted);
font-size: 0.875rem;
}
</style>Asynchronous Filtering
Search...
<template>
<div class="demo-autocomplete-width">
<Autocomplete
v-model="selectedCharacter"
label="Search a Star Wars character"
placeholder="Search..."
search-placeholder="Search characters..."
empty-text="No results found"
:items="filteredCharacters"
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Autocomplete } from '@heroui-vue/vue'
const selectedCharacter = ref<string | number | null>(null)
const characters = [
{ id: 'luke', label: 'Luke Skywalker' },
{ id: 'leia', label: 'Leia Organa' },
{ id: 'han', label: 'Han Solo' },
{ id: 'vader', label: 'Darth Vader' },
{ id: 'obi-wan', label: 'Obi-Wan Kenobi' },
]
const filteredCharacters = computed(() => characters)
</script>
<style lang="less">
.demo-autocomplete-width {
width: 16rem;
}
</style>Disabled
California
<template>
<div class="demo-autocomplete-disabled">
<Autocomplete
default-selected-key="california"
is-disabled
label="State"
placeholder="Select one"
:items="states"
/>
<Autocomplete
:default-selected-keys="['argentina', 'japan', 'france']"
is-disabled
label="Countries to Visit"
placeholder="Select countries"
selection-mode="multiple"
:items="countries"
/>
</div>
</template>
<script setup lang="ts">
import { Autocomplete } from '@heroui-vue/vue'
const states = [
{ id: 'florida', label: 'Florida' },
{ id: 'delaware', label: 'Delaware' },
{ id: 'california', label: 'California' },
{ id: 'texas', label: 'Texas' },
{ id: 'new-york', label: 'New York' },
{ id: 'washington', label: 'Washington' },
]
const countries = [
{ id: 'argentina', label: 'Argentina' },
{ id: 'venezuela', label: 'Venezuela' },
{ id: 'japan', label: 'Japan' },
{ id: 'france', label: 'France' },
{ id: 'italy', label: 'Italy' },
{ id: 'spain', label: 'Spain' },
]
</script>
<style lang="less">
.demo-autocomplete-disabled {
display: flex;
width: 16rem;
flex-direction: column;
gap: 1rem;
}
</style>API
Autocomplete Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | number | null | undefined | Controlled selected key. |
defaultSelectedKey | string | number | null | null | Initial uncontrolled selected key. |
defaultSelectedKeys | (string | number)[] | [] | Initial uncontrolled selected keys for multiple mode. |
items | AutocompleteItem[] | [] | Options rendered in the listbox. |
disabledKeys | (string | number)[] | [] | Option keys that cannot be selected. |
label | string | undefined | Field label. |
description | string | undefined | Helper text shown below the trigger. |
errorMessage | string | undefined | Error text shown when the field is invalid. |
placeholder | string | 'Select an option' | Placeholder shown before selection. |
searchPlaceholder | string | 'Search...' | Placeholder shown in the popover search field. |
selectionMode | 'single' | 'multiple' | 'single' | Whether one or multiple options can be selected. |
variant | 'primary' | 'secondary' | 'primary' | Trigger visual style. |
fullWidth | boolean | false | Stretch to parent width. |
clearable | boolean | true | Show clear button when an option is selected. |
isDisabled | boolean | false | Disable the trigger and listbox. |
isInvalid | boolean | false | Apply invalid field state. |
isRequired | boolean | false | Mark the field as required. |
isOpen | boolean | undefined | Control the popover open state. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | number | (string | number)[] | null | Emitted when selected keys change. |
update:isOpen | boolean | Emitted when the popover open state changes. |
open-change | boolean | Emitted after the popover opens or closes. |
change | (value, item) | Emitted with selected key(s) and item object(s). |
clear | void | Emitted when the clear button is pressed. |
Slots
| Slot | Props | Description |
|---|---|---|
default | - | Custom selected value rendering. |
item | { item, selected } | Custom listbox item rendering. |
indicator | { className, isOpen, isDisabled } | Custom trigger indicator. |