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.
| Prop | Type | Default | Description |
|---|---|---|---|
items | readonly any[] | — | Options to filter. Objects need itemToStringValue. Grouped lists use { value, items }[]. |
value | string | — | Controlled input text. |
defaultValue | string | — | Uncontrolled initial input text. |
onValueChange | (value: string) => void | — | Fires when the input text changes, including picking an item. |
disabled | boolean | false | Disables the input and prevents opening the list. |
itemToStringValue | (item: any) => string | — | Maps an object item to the string written into the input. Required for non-string items. |
open | boolean | — | Controlled popup visibility. |
defaultOpen | boolean | false | Uncontrolled initial popup visibility. |
openOnInputClick | boolean | false | Opens 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.
| Prop | Type | Default | Description |
|---|---|---|---|
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". |
className | string | — | Merges 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.
| Prop | Type | Default | Description |
|---|---|---|---|
side | "top" | "bottom" | "left" | "right" | "bottom" | Side of the input to place the popup. |
align | "start" | "center" | "end" | "start" | Alignment along the chosen side. |
sideOffset | number | 4 | Gap between the input and the popup, in pixels. |
container | HTMLElement | null | — | Mount the portal into a different document or element than document.body. |
className | string | — | Merges 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. PassopenOnInputClickif a click in the input should open it too. - Name the input with a
FieldLabeloraria-label. Autocomplete does not ship a default accessible name. AutocompleteClearandAutocompleteTriggerare buttons. Passaria-labelso 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.