---
title: Autocomplete
description: Autocomplete filters a large option set as the user types. It is the right choice when the full list is too long to scan in a static select.
---

import { meta as AutocompleteMeta, Default } from '@/content/stories/autocomplete';
import Canvas from '@/components/component-docs/canvas';
import Demo from '@/components/component-docs/demo';

export const config = {
	dependencies: {
		components: [Canvas, Demo],
		scripts: [
			'../../../components/component-docs/demo.script.tsx',
			'../../../components/component-docs/canvas.script.tsx',
			'../../../components/component-docs/controls.script.tsx',
		],
	},
};


# Autocomplete

<p class="docs-lede">Autocomplete filters a large option set as the user types. It is the right choice when the full list is too long to scan in a static select.</p>

## Try it

<Demo of={Default} meta={AutocompleteMeta} />

## Usage

Wrap a text input, option collection, and empty state inside `RuiAutocomplete`. Pair it with `RuiListbox` and `RuiListboxOption` for the filtered results.

```tsx
import { RuiAutocomplete, RuiAutocompleteInput, RuiAutocompleteCollection, RuiAutocompleteEmpty } from '@ecopages/radiant-ui/autocomplete';
import { RuiListbox, RuiListboxOption } from '@ecopages/radiant-ui/listbox';

<RuiAutocomplete sensitivity="base">
  <RuiAutocompleteInput placeholder="Search animals" />
  <RuiAutocompleteCollection>
    <RuiListbox>
      <RuiListboxOption value="cat">Cat</RuiListboxOption>
      <RuiListboxOption value="dog">Dog</RuiListboxOption>
    </RuiListbox>
  </RuiAutocompleteCollection>
  <RuiAutocompleteEmpty>No matches found.</RuiAutocompleteEmpty>
</RuiAutocomplete>
```

## Custom markup

`<rui-autocomplete>` coordinates any light-DOM tree that matches its query contract. The `RuiAutocomplete` helpers stamp these targets; they are not required.

```tsx
import '@ecopages/radiant-ui/autocomplete';

<rui-autocomplete sensitivity="base">
  <input type="search" data-autocomplete-input class="rui-autocomplete__input" placeholder="Search animals" />
  <div data-autocomplete-collection class="rui-autocomplete__collection">
    <div role="option" data-label="Cat">Cat</div>
    <div role="option" data-label="Dog">Dog</div>
  </div>
  <div data-autocomplete-empty class="rui-autocomplete__empty" hidden>No matches found.</div>
</rui-autocomplete>
```

BEM classes are presentation-only. Filterable items may use `[role="option"]`, `[role="menuitem"]`, or `[data-tag]`. Match text uses `data-label` or trimmed text content.

## Tune filter sensitivity

<p>Use `base` for standard substring matching. Choose `accent` or `case` when your locale or dataset requires looser or stricter comparison.</p>

## Control the input value

<p>Bind `inputValue` when you need to reset the field after selection or sync with external search state.</p>

## Theming

Autocomplete surfaces map to **semantic surface roles**, never Tailwind palette steps:

| Part | CSS roles |
| --- | --- |
| Input (`rui-autocomplete__input`) | `border`, `background`, `radius-control`, `on-background`, `space-control-*`; `focus-ring` ring |
| Collection region | structural only (scroll + flex) |
| Empty state (`rui-autocomplete__empty`) | `on-surface` |

The filtered results themselves are `RuiListbox` / `RuiListboxOption` and follow the listbox surface roles. Override at the theme layer, not in component CSS.

## API

`RuiAutocomplete` is a custom element (`<rui-autocomplete>`) that filters its composed collection; the view helpers own the composed input and empty-state markup.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `sensitivity` | `base` · `case` · `accent` | `base` | Filter sensitivity for substring matching. |
| `input-value` | `string` | `''` | Controlled filter query; when unset, reads from the composed input. |

### Light-DOM contract

| Target | Required | Host writes | Author owns |
| --- | --- | --- | --- |
| `[data-autocomplete-input]` | yes (or external on combobox/select) | — | search input value |
| `[data-autocomplete-collection]` | no | — | collection wrapper; host falls back to itself |
| `[role="option"]`, `[role="menuitem"]`, `[data-tag]` | per item | `hidden` | item content; optional `data-label` for filter text |
| `[data-autocomplete-empty]` | no | `hidden` | no-results region |
| `[data-label]` | per item | — | filter text; fallback trimmed `textContent` |

Nested hosts: items are often listbox options or tag chips; this host queries roles / `[data-tag]` only.

### View helpers

| Component | Target stamped | Notes |
| --- | --- | --- |
| `RuiAutocomplete` | `[data-ref="root"]` wrapper | Presentation only on root. |
| `RuiAutocompleteInput` | `[data-autocomplete-input]` | Bordered search input. |
| `RuiAutocompleteCollection` | `[data-autocomplete-collection]` | Scrollable filterable region. |
| `RuiAutocompleteEmpty` | `[data-autocomplete-empty]` | Hidden while matches exist. |

### CSS classes

Public BEM classes (documented via `@cssclass`):

| Class | Description |
| --- | --- |
| `.rui-autocomplete` | Filter host. |
| `.rui-autocomplete__input` | Bordered search input. |
| `.rui-autocomplete__collection` | Scrollable filterable region. |
| `.rui-autocomplete__empty` | No-results state. |

### Theme roles

| Part | CSS variables consumed |
| --- | --- |
| Input | `--border`, `--background`, `--radius-control`, `--on-background`, `--space-control-x`, `--space-control-y`, `--focus-ring` |
| Empty state | `--on-surface` |

## Accessibility

- The input exposes combobox semantics with `aria-expanded` tied to the listbox visibility.
- Keyboard users can move through options with arrow keys; Enter selects the focused option.
- Provide an accessible label via `RuiLabel` or `aria-label` on the input.
