Skip to content

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

Select one
Select a country

<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

CaliforniaTexas

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
ArgentinaJapanFrance

<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

PropTypeDefaultDescription
modelValuestring | number | nullundefinedControlled selected key.
defaultSelectedKeystring | number | nullnullInitial uncontrolled selected key.
defaultSelectedKeys(string | number)[][]Initial uncontrolled selected keys for multiple mode.
itemsAutocompleteItem[][]Options rendered in the listbox.
disabledKeys(string | number)[][]Option keys that cannot be selected.
labelstringundefinedField label.
descriptionstringundefinedHelper text shown below the trigger.
errorMessagestringundefinedError text shown when the field is invalid.
placeholderstring'Select an option'Placeholder shown before selection.
searchPlaceholderstring'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.
fullWidthbooleanfalseStretch to parent width.
clearablebooleantrueShow clear button when an option is selected.
isDisabledbooleanfalseDisable the trigger and listbox.
isInvalidbooleanfalseApply invalid field state.
isRequiredbooleanfalseMark the field as required.
isOpenbooleanundefinedControl the popover open state.

Events

EventPayloadDescription
update:modelValuestring | number | (string | number)[] | nullEmitted when selected keys change.
update:isOpenbooleanEmitted when the popover open state changes.
open-changebooleanEmitted after the popover opens or closes.
change(value, item)Emitted with selected key(s) and item object(s).
clearvoidEmitted when the clear button is pressed.

Slots

SlotPropsDescription
default-Custom selected value rendering.
item{ item, selected }Custom listbox item rendering.
indicator{ className, isOpen, isDisabled }Custom trigger indicator.

Released under the Apache-2.0 License.