Skip to content

Autocomplete ​

An autocomplete is a Select with a search box in its popover. The trigger shows the chosen option rather than a text field, which is the difference from a ComboBox: here the reader is always choosing from the list, and the typing only narrows it.

ts
import {
  Autocomplete,
  AutocompleteFilter,
  AutocompleteIndicator,
  AutocompletePopover,
  AutocompleteTrigger,
  AutocompleteValue,
} from "ropav";

It takes every prop a select takes, items included — that is what answers for the options while the popover is shut and nothing has been rendered yet.

Destination
Pick a city
autocomplete-basic.vue
vue
<script setup lang="ts">
import {
  Autocomplete,
  AutocompleteFilter,
  AutocompleteIndicator,
  AutocompletePopover,
  AutocompleteTrigger,
  AutocompleteValue,
  Label,
  ListBox,
  ListBoxItem,
  ListBoxItemIndicator,
  SearchField,
  SearchFieldClearButton,
  SearchFieldGroup,
  SearchFieldInput,
  SearchFieldSearchIcon,
  useFilter,
} from "ropav";

const cities = [
  { id: "lisbon", name: "Lisbon" },
  { id: "osaka", name: "Osaka" },
  { id: "quito", name: "Quito" },
  { id: "tallinn", name: "Tallinn" },
];

const byName = (city: { name: string }) => city.name;

const filter = useFilter({ sensitivity: "base" });
</script>

<template>
  <Autocomplete
    class="field"
    :item-text-value="byName"
    :items="cities"
    placeholder="Pick a city"
    selection-mode="single"
  >
    <Label>Destination</Label>
    <AutocompleteTrigger>
      <AutocompleteValue />
      <AutocompleteIndicator />
    </AutocompleteTrigger>

    <AutocompletePopover>
      <AutocompleteFilter v-slot="{ items: matches }" :filter="filter.contains">
        <SearchField auto-focus aria-label="Search cities" name="search" variant="secondary">
          <SearchFieldGroup>
            <SearchFieldSearchIcon />
            <SearchFieldInput placeholder="Search…" />
            <SearchFieldClearButton />
          </SearchFieldGroup>
        </SearchField>

        <ListBox>
          <ListBoxItem v-for="city in matches" :id="city.id" :key="city.id" :text-value="city.name">
            {{ city.name }}
            <ListBoxItemIndicator />
          </ListBoxItem>
        </ListBox>
      </AutocompleteFilter>
    </AutocompletePopover>
  </Autocomplete>
</template>

<style scoped>
.field {
  width: calc(var(--rp-spacing) * 64);
}
</style>

AutocompleteFilter is the search box inside the popover, and AutocompleteClearButton empties the selection — which emits clear rather than taking an onClear prop, because Vue would route a listener of that name to an emit anyway and fire the handler twice.

Which of the three to reach for ​

  • A Select when the list is short enough to read.
  • An autocomplete when it is long but still a fixed list — the reader is choosing, not writing.
  • A ComboBox when what they type may itself be the answer.

Accessibility ​

  • AutocompleteTrigger is a role="group" holding the value and the button that opens the popover, and that button is labelled by both — so a reader hears the current choice and the field's name together, rather than an empty text field they might think they have to fill in.
  • Focus moves into the filter when the popover opens and back to the trigger when it closes, so typing goes where the reader expects without them having to find it.
  • selection-mode="multiple" announces each option's selected state as it is toggled; the trigger then summarises what was chosen, which is the part that has to stay readable as the list grows.

API ​

Autocomplete

PropTypeDefaultDescription
allowsEmptyCollectionboolean—Whether the popover may open with no options in it.
ariaDescribedbystring—
ariaLabelstring—
ariaLabelledbystring—
autoCompletestring—What the browser may offer to autofill.
defaultOpenboolean—Whether the popover starts open.
defaultValueSelectedValue—The key(s) the select starts on.
disabledBehavior"all" | "selection""all"Whether a disabled key is also unfocusable.
disabledKeysIterable<CollectionKey>—Keys that cannot be chosen.
formstring—Id of the <form> the hidden control belongs to.
fullWidthboolean—Whether the autocomplete stretches to fill its container.
isDisabledboolean—
isInvalidboolean—Controlled validity. Present at all — true or false — shadows validate.
isOpenboolean—Whether the popover is open. Controlled.
isRequiredboolean—
itemDisabled((item: T) => boolean)—Whether an item cannot be chosen. Merges with disabledKeys.
itemKey((item: T, index: number) => CollectionKey)—An item's key. Defaults to its own id, then key, then its index.
items*readonly T[]—The options, as data. **Required.** The React build reads its options out of a hidden render pass of its children. Rendering is what creates DOM in vapor, so there is no such pass — and the options only exist in the DOM while the popover is open. Everything a closed select has to answer for reads this instead: the value in the trigger, the options of the hidden native control, typing to choose, and the arrow keys stepping through choices. Required rather than optional so leaving it out is a type error instead of a select that quietly shows a placeholder over a value it holds.
itemTextValue((item: T) => string | undefined)—Text shown for an item in the trigger, and matched on when typing. Defaults to its text.
namestring—Name of the hidden native control, for form submission.
placeholderstring—Text shown in the trigger when nothing is chosen. Defaults to a localized string.
selectionMode"single" | "multiple""single"Whether one or several options can be chosen.
shouldCloseOnSelectbooleantrue when singleWhether choosing an option closes the popover.
size"xs" | "sm" | "md" | "lg""md"How tall the trigger stands, matching a button of the same size. Handed to the listbox in the popover, unless it declares one of its own.
validateValidationFunction<SelectedValue>—
validationBehavior"aria" | "native""native"
valueSelectedValue—The chosen key, or keys when multiple. Controlled.
variant"primary" | "secondary""primary"Visual variant.

The default slot receives:

Slot propType
isDisabledboolean
isFocusedboolean
isInvalidboolean
isOpenboolean
isRequiredboolean
EventPayload
changeSelectedValue
clear—
focusChangeboolean
openChangeboolean
update:isOpenboolean
update:valueSelectedValue

AutocompleteClearButton

AutocompleteClearButton takes nothing but class.

AutocompleteFilter

PropTypeDefaultDescription
defaultInputValuestring—Text the search field starts with.
disableAutoFocusFirstboolean—Whether typing stops moving focus onto the first matching option.
disableVirtualFocusboolean—Whether the arrows move real focus into the options instead of a nominal one. Off is the whole arrangement: the caret stays in the search field and the field names the focused option, so typing and choosing can go on in one breath.
filter((textValue: string, inputValue: string) => boolean)—Whether an option's text matches what has been typed. Without one nothing is filtered. useFilter is where the three usual answers come from, and contains is what the React build's stories pass.
inputValuestring—Text in the search field. Present at all puts the caller in charge of it.
itemsreadonly T[]—The options to show, already narrowed. Takes the filtering over entirely. This is the seam an asynchronous search or a virtualized list needs: the caller fetches or slices, and filter is then never consulted.

Slots: default.

EventPayload
inputChangestring
update:inputValuestring

AutocompleteIndicator

Slots: default.

AutocompletePopover

PropTypeDefaultDescription
arrowBoundaryOffsetnumber—
containerPaddingnumber—
crossOffsetnumber—Shift along the trigger's edge, in pixels.
isKeyboardDismissDisabledboolean—
offsetnumber8Distance from the trigger, in pixels.
placement"bottom" | "bottom end" | "bottom left" | "bottom right" | "bottom start" | "end" | "end bottom" | "end top" | "left" | "left bottom" | "left top" | "right" | "right bottom" | "right top" | "start" | "start bottom" | "start top" | "top" | "top end" | "top left" | "top right" | "top start""bottom"
shouldFlipboolean—Whether the popover may flip to the other side when it does not fit.

Slots: default.

AutocompleteTrigger

PropTypeDefaultDescription
isDisabledboolean—Whether this trigger in particular is disabled. Falls back to the autocomplete's own state.

The default slot receives:

Slot propType
isDisabledboolean
isFocusWithinboolean
isHoveredboolean
isInvalidboolean

AutocompleteValue

Slots: default.