Autocomplete
A text input paired with a filtered list of suggestions, from @clerk/headless. It is a
headless primitive: it supplies input text, selected value, and open state, portalling,
floating-ui positioning, virtual-focus keyboard navigation, and ARIA combobox wiring, but
ships no styles — you bring your own CSS by targeting the data-* attributes each
part emits. It does no filtering of its own: you read inputValue and render the
surviving options.
Example
The demo below is intentionally unstyled — it renders the raw primitive so you can see its behavior and ARIA wiring. Type to filter the consumer-supplied list; arrow keys move the active option and Enter selects it.
Usage
import { Autocomplete } from '@clerk/headless/autocomplete';
const [inputValue, setInputValue] = useState('');
const filtered = items.filter(item => item.toLowerCase().includes(inputValue.toLowerCase()));
<Autocomplete.Root
inputValue={inputValue}
onInputValueChange={setInputValue}
>
<Autocomplete.Input placeholder='Search…' />
<Autocomplete.Portal>
<Autocomplete.Positioner>
<Autocomplete.Popup>
<Autocomplete.List>
{filtered.map(item => (
<Autocomplete.Option
key={item}
value={item}
label={item}
>
{item}
</Autocomplete.Option>
))}
</Autocomplete.List>
</Autocomplete.Popup>
</Autocomplete.Positioner>
</Autocomplete.Portal>
</Autocomplete.Root>;Parts
| Part | Default Element | Description |
|---|---|---|
Autocomplete.Root | none (context) | Owns input text, selected value, open state, and positioning |
Autocomplete.Input | <input> | The combobox text input that drives filtering and navigation |
Autocomplete.Portal | none (portal) | Portals its children; renders nothing until mounted |
Autocomplete.Positioner | <div> | Floating-positioned container |
Autocomplete.Popup | <div> | Visual wrapper for the option list |
Autocomplete.List | <div> | Listbox container (use inline, inside another floating surface) |
Autocomplete.Option | <div> | A selectable option (role="option") |
Autocomplete.Arrow | <svg> | Optional arrow pointing at the input |
All rendered parts accept a render prop for polymorphic rendering and standard HTML
attributes for their default element. Autocomplete.Arrow accepts floating-ui
FloatingArrow props. Mounting Autocomplete.List switches the primitive into inline mode
(Escape / outside press bubble to the parent floating surface instead of dismissing).
Props
Autocomplete.Root
| Prop | Type | Default | Description |
|---|---|---|---|
inputValue | string | — | Controlled input text |
defaultInputValue | string | '' | Initial input text (uncontrolled) |
onInputValueChange | (value: string) => void | — | Called when the input text changes |
value | string | — | Controlled selected value |
defaultValue | string | — | Initial selected value (uncontrolled) |
onValueChange | (value: string) => void | — | Called when an option is selected |
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial open state (uncontrolled) |
onOpenChange | (open: boolean) => void | — | Called when the open state changes |
placement | Placement | 'bottom-start' | Placement relative to the input |
sideOffset | number | 4 | Gap in px between the input and the popup |
Autocomplete.Option
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — (required) | The option's value |
label | string | falls back to value | Label written to the input when selected |
disabled | boolean | — | Prevent selection |
Autocomplete.Input, Autocomplete.Positioner, Autocomplete.Popup, and
Autocomplete.List take no additional props beyond standard HTML attributes for their
default element. Autocomplete.Portal accepts root.
Styling
Each part emits data-* attributes you can target with any CSS solution:
| Attribute | Applies To | Description |
|---|---|---|
data-open | Input | Present when the popup is open |
data-closed | Input | Present when the popup is closed |
data-selected | Option | Present on the option matching the selected value |
data-active | Option | Present on the keyboard-highlighted option |
data-disabled | Option | Present on a disabled option |
data-side | Positioner, Arrow | Resolved side: top / bottom / left / right |
The positioner exposes anchor/viewport geometry as CSS variables (and sets an inline
width matching the input and a max-height of the available viewport height):
| Variable | Description |
|---|---|
--cl-anchor-width | Input width |
--cl-anchor-height | Input height |
--cl-available-width | Available width between the anchor and viewport edge |
--cl-available-height | Available height between the anchor and viewport edge |
--cl-transform-origin | transform-origin pointing back toward the anchor |