Autocomplete

A Field input that filters suggestions as you type and still accepts free-form text.

Usage

import {
  Autocomplete,
  AutocompleteContent,
  AutocompleteEmpty,
  AutocompleteInput,
  AutocompleteItem,
  AutocompleteList,
} from '@epilot/spark-ui/autocomplete'
import { Field, FieldLabel } from '@epilot/spark-ui/field'
function DeviceSearch() {
  return (
    <Field>
      <FieldLabel>Device</FieldLabel>
      <Autocomplete items={devices}>
        <AutocompleteInput placeholder="Search devices" />
        <AutocompleteContent>
          <AutocompleteEmpty>No devices found</AutocompleteEmpty>
          <AutocompleteList>
            {(device: string) => (
              <AutocompleteItem key={device} value={device}>
                {device}
              </AutocompleteItem>
            )}
          </AutocompleteList>
        </AutocompleteContent>
      </Autocomplete>
    </Field>
  )
}

Compose Autocomplete inside a Field the same way as Input. The field looks identical; Autocomplete adds a filtered list of options. Use Autocomplete when the user types to filter suggestions and may keep a value that is not in the list. Reach for Select when the value must be one of a closed set of options and the input cannot be free-form.

AutocompleteList can take a render function over items on the root, so you do not map the array yourself. Pass itemToStringValue on the root when items are objects, so the input shows a label rather than [object Object].

Empty, status, and button labels are yours to pass. Autocomplete ships no English defaults.

Examples

Open on click

Pass openOnInputClick so the list opens when the input is clicked, not only after typing. Arrow Down from the input also opens it. Compose AutocompleteTrigger when you want a chevron instead.

Groups

Pass grouped items ({ value, items }) and render each group with AutocompleteGroup, AutocompleteGroupLabel, and AutocompleteCollection.

Label position

The input is the same Field composition as Input: outset puts the label above, inset puts it inside the shell. The input picks up the field's id, inputStyle, and aria-describedby, and goes bare in inset so the shell carries the box.

Hidden label

Pass srOnly to FieldLabel to hide it visually while keeping it in the accessibility tree; it still names and focuses the control. Prefer this over dropping the label when the surrounding UI already makes the purpose obvious. Without a FieldLabel, pass aria-label on AutocompleteInput.

Clear and trigger

AutocompleteInputGroup is the same box as InputGroup when you add a clear button or a chevron that opens the list. AutocompleteInput inside it drops its own chrome so the group carries the Field chrome. AutocompleteClear and AutocompleteTrigger need an aria-label from your app.

Disabled

Set data-disabled on the Field and disabled on the Autocomplete root: the label stays full contrast and the input is grayed out and non-interactive.

API Reference

Built on Base UI Autocomplete. See the Base UI docs for the full API (items, value, defaultValue, onValueChange, mode, autoHighlight, limit, itemToStringValue, openOnInputClick, useFilter, …). Spark notes:

Autocomplete

Root state. Pass items so the list can filter and render from a function child. disabled disables the input and the popup.

PropTypeDefaultDescription
itemsreadonly any[]Options to filter. Objects need itemToStringValue. Grouped lists use { value, items }[].
valuestringControlled input text.
defaultValuestringUncontrolled initial input text.
onValueChange(value: string) => voidFires when the input text changes, including picking an item.
disabledbooleanfalseDisables the input and prevents opening the list.
itemToStringValue(item: any) => stringMaps an object item to the string written into the input. Required for non-string items.
openbooleanControlled popup visibility.
defaultOpenbooleanfalseUncontrolled initial popup visibility.
openOnInputClickbooleanfalseOpens the list on a click in the input, before the user types.

AutocompleteInput

The text field. Renders Spark Input, so it looks the same as Input in a Field. variant and Field chrome match the rest of the input family. className merges with the Input styles.

PropTypeDefaultDescription
variant"soft" | "surface" | "outlined" | "ghost" | "none"follows the enclosing Field, else the data-input-style knob (soft)Input box chrome. Inside AutocompleteInputGroup this defaults to "none".
classNamestringMerges with the Input styles.

AutocompleteInputGroup

Use when the input shares a box with AutocompleteClear and/or AutocompleteTrigger. Applies the same chrome as InputGroup (the same Field box as Input). Inputs inside go chrome-less automatically.

AutocompleteContent

Renders its own portal and positions the popup under the input, matching the input width. Height-capped and scrollable.

PropTypeDefaultDescription
side"top" | "bottom" | "left" | "right""bottom"Side of the input to place the popup.
align"start" | "center" | "end""start"Alignment along the chosen side.
sideOffsetnumber4Gap between the input and the popup, in pixels.
containerHTMLElement | nullMount the portal into a different document or element than document.body.
classNamestringMerges with the popup styles.

AutocompleteList

The listbox. Pass a function child (item) => <AutocompleteItem /> when items is on the root, or static children. For grouped items, the function receives each group.

AutocompleteItem

One suggestion. value must match an entry in items. Highlighted with data-highlighted while keyboard or pointer active.

AutocompleteEmpty

Renders only when the filtered list is empty. Pass the empty copy from your app; there is no default string.

AutocompleteStatus

Optional status line in the popup (async search progress, "showing 8 of 40"). Pass the copy from your app.

AutocompleteGroup / AutocompleteGroupLabel / AutocompleteCollection

Section a grouped list. AutocompleteGroup takes that group's items. AutocompleteCollection iterates them for AutocompleteItem.

AutocompleteClear / AutocompleteTrigger

Icon buttons for the input group. Childless AutocompleteClear renders a close icon; childless AutocompleteTrigger renders a chevron. Both need an aria-label from your app. AutocompleteClear only mounts when the input has a value.

useAutocompleteFilter / useAutocompleteFilteredItems

Re-exports of Base UI Autocomplete.useFilter and Autocomplete.useFilteredItems for async or custom filtering. See the Base UI docs for the filter API.

Accessibility

  • The input is a combobox. Arrow keys move the highlight in the list, Enter chooses the highlighted item, Escape closes the popup. Typing filters the list. Pass openOnInputClick if a click in the input should open it too.
  • Name the input with a FieldLabel or aria-label. Autocomplete does not ship a default accessible name.
  • AutocompleteClear and AutocompleteTrigger are buttons. Pass aria-label so they are announced; the default icons are decorative.
  • Do not use Autocomplete as the only way to complete a required choice if the value must come from the list. Use Select for a closed set of options.

On this page