Autocomplete
An input that suggests matching options while preserving free-form text.
API Reference
Original primitive API
Behavior, accessibility details, and low-level props are documented by Base UI.
Choosing the right component
Use Autocomplete when users need to type freely and you want to suggest matching options while
they type.
- Choose
Autocompletefor free-form values with suggestions. - Choose
Comboboxwhen users should select from predefined options, with typing used to filter. - Choose
Selectfor strict pickers with no text input in the trigger.
Basic
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function AutocompleteDemo() { const id = useId(); return ( <Autocomplete items={tags} itemToStringValue={(item) => item.value}> <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Search tags</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. feature" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const tags = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" },];Full list of component variables available for project-level overrides.
| Property | Default | Description |
|---|---|---|
| --autocomplete-action-bg-hover | var(--color-muted) | Controls trigger and clear hover background. |
| --autocomplete-action-color-hover | var(--color-foreground) | Controls trigger and clear hover color. |
| --autocomplete-action-radius | var(--radius-sm) | Controls trigger and clear button radius. |
| --autocomplete-action-size | 1.5rem | Controls trigger and clear button size. |
| --autocomplete-actions-gap | 0.125rem | Controls spacing between trigger and clear buttons. |
| --autocomplete-actions-offset-right | 0.5rem | Controls right offset for control actions. |
| --autocomplete-arrow-height | 0.625rem | Controls optional popup arrow height. |
| --autocomplete-arrow-inline-offset | 13px | Controls arrow inline-side offset. |
| --autocomplete-arrow-size | 8px | Controls arrow side offset size. |
| --autocomplete-arrow-stroke-color | var(--autocomplete-popup-border-color) | Controls optional popup arrow border color. |
| --autocomplete-arrow-width | 1.25rem | Controls optional popup arrow width. |
| --autocomplete-backdrop-bg | var(--backdrop-bg, var(--color-overlay)) | Controls optional backdrop color. |
| --autocomplete-backdrop-blur | 2px | Controls optional backdrop blur. |
| --autocomplete-backdrop-transition | var(--transition-default) | Controls optional backdrop animation. |
| --autocomplete-bg | var(--color-background) | Controls control background. |
| --autocomplete-border-color | var(--color-border) | Controls control border color. |
| --autocomplete-border-width | var(--border-width-sm) | Controls control border width. |
| --autocomplete-color | var(--color-foreground) | Controls primary text color. |
| --autocomplete-control-height | var(--size-lg) | Controls input and trigger height. |
| --autocomplete-empty-color | var(--color-muted-foreground) | Controls empty state text color. |
| --autocomplete-empty-font-size | var(--text-sm) | Controls empty-state font size. |
| --autocomplete-empty-line-height | var(--line-height-text-sm) | Controls empty-state line height. |
| --autocomplete-empty-padding-x | 1rem | Controls empty-state horizontal padding. |
| --autocomplete-empty-padding-y | 0.75rem | Controls empty-state vertical padding. |
| --autocomplete-field-gap | 0.375rem | Controls spacing between field label and control. |
| --autocomplete-field-trigger-bg-open | var(--color-muted) | Controls field-trigger background when popup is open. |
| --autocomplete-focus-ring-color | var(--color-ring) | Controls keyboard focus ring color. |
| --autocomplete-focus-ring-offset | var(--autocomplete-border-width) | Controls focus ring offset. |
| --autocomplete-focus-ring-width | var(--autocomplete-border-width) | Controls focus ring width. |
| --autocomplete-grid-item-min-height | 2.5rem | Controls grid item minimum height. |
| --autocomplete-grid-item-padding-x | var(--spacing-2) | Controls grid item horizontal padding. |
| --autocomplete-grid-item-width | 2.5rem | Controls grid item width. |
| --autocomplete-group-label-bg | var(--color-popover) | Controls group label background. |
| --autocomplete-group-label-color | var(--color-muted-foreground) | Controls group label color. |
| --autocomplete-group-label-font-size | var(--text-xs) | Controls group label font size. |
| --autocomplete-group-label-font-weight | var(--weight-semibold) | Controls group label font weight. |
| --autocomplete-group-label-line-height | var(--line-height-text-xs) | Controls group label line height. |
| --autocomplete-group-label-padding-bottom | 0.35rem | Controls group label bottom padding. |
| --autocomplete-group-label-padding-top | 0.35rem | Controls group label top padding. |
| --autocomplete-group-label-padding-x | 0.625rem | Controls group label horizontal padding. |
| --autocomplete-group-padding-bottom | var(--spacing-1) | Controls group bottom padding. |
| --autocomplete-highlight-bg | var(--color-foreground) | Controls highlighted item background. |
| --autocomplete-highlight-color | var(--color-background) | Controls highlighted item text color. |
| --autocomplete-highlight-inset-x | var(--spacing-1) | Controls highlighted item horizontal inset. |
| --autocomplete-highlight-radius | var(--radius-sm) | Controls highlighted item radius. |
| --autocomplete-icon-color | var(--color-muted-foreground) | Controls default icon color. |
| --autocomplete-icon-size | 0.875rem | Controls icon container size. |
| --autocomplete-icon-svg-size | 1rem | Controls icon glyph size. |
| --autocomplete-inline-input-border-width | var(--border-width-sm) | Controls inline input border width. |
| --autocomplete-inline-input-container-padding-bottom | var(--spacing-2) | Controls inline input container bottom padding. |
| --autocomplete-inline-input-container-padding-top | var(--spacing-2) | Controls inline input container top padding. |
| --autocomplete-inline-input-container-padding-x | var(--spacing-2) | Controls inline input container horizontal padding. |
| --autocomplete-inline-input-divider-color | var(--autocomplete-border-color) | Controls inline input container divider color. |
| --autocomplete-inline-input-divider-style | solid | Controls inline input container divider style. |
| --autocomplete-inline-input-divider-width | var(--border-width-sm) | Controls inline input container divider width. |
| --autocomplete-inline-input-padding-x | 0.75rem | Controls inline input horizontal padding. |
| --autocomplete-inline-input-radius | var(--radius-sm) | Controls inline input radius. |
| --autocomplete-inline-list-padding-bottom | 0.5rem | Controls inline list bottom padding. |
| --autocomplete-inline-list-padding-top | 0.375rem | Controls inline list top padding. |
| --autocomplete-inline-list-scroll-padding-bottom | 0.5rem | Controls inline list bottom scroll padding. |
| --autocomplete-inline-list-scroll-padding-top | 0.375rem | Controls inline list top scroll padding. |
| --autocomplete-input-group-padding-x | 0 | Controls horizontal input-group padding. |
| --autocomplete-input-padding-x-end | 3.25rem | Controls input end padding. |
| --autocomplete-input-padding-x-start | 0.875rem | Controls input start padding. |
| --autocomplete-input-placeholder-color | var(--color-muted-foreground) | Controls placeholder color. |
| --autocomplete-item-color | var(--color-foreground) | Controls item text color. |
| --autocomplete-item-font-size | var(--text-sm) | Controls item font size. |
| --autocomplete-item-line-height | var(--line-height-text-sm) | Controls item line height. |
| --autocomplete-item-min-height | 2rem | Controls item minimum height. |
| --autocomplete-item-padding-x | var(--spacing-4) | Controls item start padding. |
| --autocomplete-item-padding-x-end | 1rem | Controls item end padding. |
| --autocomplete-item-padding-y | var(--spacing-2) | Controls item vertical padding. |
| --autocomplete-item-text-content-gap | var(--spacing-2) | Controls gap between item text content slots. |
| --autocomplete-item-text-icon-color | currentColor | Controls item text icon color. |
| --autocomplete-item-text-icon-size | 1rem | Controls item text icon size. |
| --autocomplete-label-font-size | var(--text-sm) | Controls field label font size. |
| --autocomplete-label-font-weight | var(--weight-medium) | Controls field label font weight. |
| --autocomplete-label-line-height | var(--line-height-text-sm) | Controls field label line height. |
| --autocomplete-list-max-height | var(--autocomplete-popup-max-height) | Controls list max height. |
| --autocomplete-list-padding-y | 0.25rem | Controls vertical list padding. |
| --autocomplete-list-scroll-padding-y | 0.25rem | Controls list scroll padding. |
| --autocomplete-popup-bg | var(--color-popover) | Controls popup background. |
| --autocomplete-popup-border-color | var(--color-border) | Controls popup border color. |
| --autocomplete-popup-border-width | var(--border-width-sm) | Controls popup border width. |
| --autocomplete-popup-max-height | 24rem | Controls the popup max height. |
| --autocomplete-radius | var(--radius-md) | Controls control and popup radius. |
| --autocomplete-row-gap | var(--spacing-1) | Controls grid row item gap. |
| --autocomplete-row-padding-x | var(--spacing-1) | Controls grid row horizontal padding. |
| --autocomplete-separator-margin-x | 1rem | Controls separator horizontal margin. |
| --autocomplete-separator-margin-y | 0.375rem | Controls separator vertical margin. |
| --autocomplete-separator-size | var(--border-width-sm) | Controls separator thickness. |
| --autocomplete-shadow | var(--shadow-lg) | Controls popup shadow. |
| --autocomplete-status-color | var(--autocomplete-empty-color) | Controls status text color. |
| --autocomplete-status-divider-color | var(--autocomplete-popup-border-color) | Controls status divider color. |
| --autocomplete-status-divider-width | var(--border-width-sm) | Controls status divider width. |
| --autocomplete-status-font-size | var(--text-xs) | Controls status font size. |
| --autocomplete-status-gap | var(--spacing-1) | Controls status content gap. |
| --autocomplete-status-line-height | var(--line-height-text-xs) | Controls status line height. |
| --autocomplete-status-padding-x | 0.75rem | Controls status horizontal padding. |
| --autocomplete-status-padding-y | 0.5rem | Controls status vertical padding. |
| --autocomplete-trigger-gap | 0.75rem | Controls field-trigger content gap. |
| --autocomplete-trigger-padding-x | 0.875rem | Controls field-trigger horizontal padding. |
| --autocomplete-width | 16rem | Controls the control and popup anchor width. |
Interactive variables scoped for docs preview without changing size scale tokens.
| Property | Value | Default | Description |
|---|---|---|---|
| --autocomplete-bg | var(--color-background) | Controls control background. | |
| --autocomplete-border-color | var(--color-border) | Controls control border color. | |
| --autocomplete-color | var(--color-foreground) | Controls primary text color. | |
| --autocomplete-empty-color | var(--color-muted-foreground) | Controls empty text color. | |
| --autocomplete-focus-ring-color | var(--color-ring) | Controls focus ring color. | |
| --autocomplete-group-label-font-weight | var(--weight-semibold) | Controls label weight. | |
| --autocomplete-highlight-bg | var(--color-foreground) | Controls highlighted item bg. | |
| --autocomplete-highlight-color | var(--color-background) | Controls highlighted item text. | |
| --autocomplete-icon-color | var(--color-muted-foreground) | Controls default icon color. | |
| --autocomplete-popup-bg | var(--color-popover) | Controls popup background. | |
| --autocomplete-popup-border-color | var(--color-border) | Controls popup border color. | |
| --autocomplete-radius | var(--radius-md) | Controls control and popup radius. | |
| --autocomplete-shadow | var(--shadow-lg) | Controls popup shadow. |
Anatomy
Autocomplete combines an input field, control actions, and a popup with filtered options. Keep
input-related parts together inside AutocompleteField so labeling and keyboard behavior stay
connected.
Autocomplete
├─ AutocompleteField
│ ├─ AutocompleteFieldLabel
│ └─ AutocompleteInputGroup
│ ├─ AutocompleteInput
│ └─ AutocompleteControlActions (optional)
│ ├─ AutocompleteClear (optional)
│ └─ AutocompleteTrigger (optional)
└─ AutocompleteContent
├─ AutocompleteStatus (optional)
├─ AutocompleteEmpty
├─ AutocompleteSeparator (optional)
└─ AutocompleteList
├─ AutocompleteItem
│ └─ AutocompleteItemText
├─ AutocompleteRow (optional, for grid lists)
│ └─ AutocompleteItem
└─ AutocompleteGroup (optional)
├─ AutocompleteGroupLabel
└─ AutocompleteCollection
└─ AutocompleteItem<Autocomplete items={tags} itemToStringValue={(item) => item.value}>
<AutocompleteField>
<AutocompleteFieldLabel htmlFor={id}>Search tags</AutocompleteFieldLabel>
<AutocompleteInputGroup>
<AutocompleteInput id={id} placeholder="e.g. feature" />
<AutocompleteControlActions>
<AutocompleteClear aria-label="Clear value" />
<AutocompleteTrigger aria-label="Open suggestions" />
</AutocompleteControlActions>
</AutocompleteInputGroup>
</AutocompleteField>
<AutocompleteContent>
<AutocompleteEmpty>No tags found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.id} value={item}>
<AutocompleteItemText>{item.value}</AutocompleteItemText>
</AutocompleteItem>
)}
</AutocompleteList>
</AutocompleteContent>
</Autocomplete>| Part | Role |
|---|---|
Autocomplete | Root state machine. Handles value, filtering, highlighting, and open/close behavior. |
AutocompleteField | Semantic wrapper for field-level structure. Keeps label and input wiring in one place. |
AutocompleteFieldLabel | Accessible label for the input or field trigger. |
AutocompleteInputGroup | Layout wrapper for the input and optional control actions. |
AutocompleteInput | Text input where users type a query or free-form value. |
AutocompleteControlActions | Optional container for clear/open controls. |
AutocompleteClear | Optional action that clears the current input value. |
AutocompleteTrigger | Optional action that toggles or opens the suggestion popup. |
AutocompleteContent | Popup surface. Also renders service slots (portal, backdrop, positioner, arrow) via props. |
AutocompleteStatus | Optional status region for loading or result-count feedback. |
AutocompleteEmpty | Empty-state content shown when no items match. |
AutocompleteList | Collection container for options. |
AutocompleteRow | Optional row wrapper when grid is enabled on the root. |
AutocompleteItem | One selectable option in the list. |
AutocompleteSeparator | Optional visual separator between groups or custom sections. |
AutocompleteGroup | Optional grouped section with its own item subset. |
AutocompleteCollection | Renderer for group items inside AutocompleteGroup. |
Use default styling for service slots in most cases. Customize portal, backdrop,
positioner, and arrow when you need custom layering, overlay behavior, popup placement, or
arrow visuals.
Composition
Use Autocomplete for root state and Base UI behavior props such as items, value,
onValueChange, filter, limit, mode, autoHighlight, and openOnInputClick.
The root keeps the Base UI behavior surface intact. Use itemToStringValue for object items,
filter={null} or filteredItems for external filtering, grid with AutocompleteRow for grid
navigation, modal for modal popup behavior, submitOnItemClick for single-field search forms,
onOpenChange for controlled popup side effects, virtualized for externally virtualized lists,
and keepHighlight or highlightItemOnHover when pointer and keyboard highlighting need custom
rules. actionsRef is available for manual unmount flows with animation libraries.
useAutocompleteFilter exposes Base UI string matching helpers, and
useAutocompleteFilteredItems returns the currently filtered items for custom list layouts.
className styles the visible popup. classNames styles service slots hidden from the default
composition: portal, backdrop, positioner, and arrow. Use container, portalProps,
backdropProps, positionerProps, arrowProps, withBackdrop, and arrow when you need the
matching Base UI escape hatches:
<AutocompleteContent
className={styles.popup}
sideOffset={8}
withArrow
withBackdrop
classNames={{
portal: styles.portal,
backdrop: styles.backdrop,
positioner: styles.positioner,
arrow: styles.arrow,
}}
/>Examples
Grouped
Pass grouped data and render AutocompleteCollection inside each AutocompleteGroup.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteCollection, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function GroupedAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={groupedTags} itemToStringValue={(item) => item.value}> <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Search grouped tags</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. docs" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(group) => ( <AutocompleteGroup key={group.value} items={group.items}> <AutocompleteGroupLabel>{group.value}</AutocompleteGroupLabel> <AutocompleteCollection> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> )} </AutocompleteCollection> </AutocompleteGroup> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const groupedTags = [ { value: "General", items: [ { id: "gt-1", value: "feature" }, { id: "gt-2", value: "fix" }, { id: "gt-3", value: "docs" }, ], }, { value: "Scope", items: [ { id: "gt-4", value: "internal" }, { id: "gt-5", value: "mobile" }, { id: "gt-6", value: "backend" }, ], },];Item Icons
Use item text slots for richer option content. Control icons can be replaced by passing children to AutocompleteTrigger, AutocompleteClear, or AutocompleteIcon.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItemText, AutocompleteItemTextContent, AutocompleteItemTextIcon, ChevronUpIcon, CloseLineIcon, InfoIcon, AutocompleteItemTextLabel, AutocompleteItem,} from "moduix";import { useId } from "react";export function ItemIconsAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={tags} itemToStringValue={(item) => item.value}> <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Search tags with icons</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. feature" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value"> <CloseLineIcon /> </AutocompleteClear> <AutocompleteTrigger aria-label="Open suggestions"> <ChevronUpIcon /> </AutocompleteTrigger> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText> <AutocompleteItemTextContent> <AutocompleteItemTextIcon> <InfoIcon /> </AutocompleteItemTextIcon> <AutocompleteItemTextLabel>{item.value}</AutocompleteItemTextLabel> </AutocompleteItemTextContent> </AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const tags = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" },];Input Inside Popup
Use AutocompleteFieldTrigger when the visible field opens a popup that contains the searchable input.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteFieldTrigger, AutocompleteValue, AutocompleteIcon, AutocompleteContent, AutocompleteInlineInputContainer, AutocompleteInput, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";export function InputInsidePopupAutocompleteDemo() { return ( <Autocomplete items={tags} itemToStringValue={(item) => item.value}> <AutocompleteField> <AutocompleteFieldLabel>Tag</AutocompleteFieldLabel> <AutocompleteFieldTrigger> <AutocompleteValue>{(value) => value || "Type to search"}</AutocompleteValue> <AutocompleteIcon /> </AutocompleteFieldTrigger> </AutocompleteField> <AutocompleteContent> <AutocompleteInlineInputContainer> <AutocompleteInput placeholder="Search tag" /> </AutocompleteInlineInputContainer> <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const tags = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" },];Limit
Set limit to cap the number of rendered matches.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function LimitAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={topMovies} itemToStringValue={(item) => item.title} limit={5} openOnInputClick > <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Top 5 matches</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="Type movie title" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No movies found.</AutocompleteEmpty> <AutocompleteList> {(movie) => ( <AutocompleteItem key={movie.id} value={movie}> <AutocompleteItemText> {movie.title} ({movie.year}) </AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const topMovies = [ { id: "1", title: "The Shawshank Redemption", year: 1994 }, { id: "2", title: "The Godfather", year: 1972 }, { id: "3", title: "The Dark Knight", year: 2008 }, { id: "4", title: "Pulp Fiction", year: 1994 }, { id: "5", title: "Forrest Gump", year: 1994 }, { id: "6", title: "Inception", year: 2010 },];Auto Highlight
Use autoHighlight="always" when the first match should be active immediately.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function AutoHighlightAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={tags} itemToStringValue={(item) => item.value} autoHighlight="always" mode="list" > <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Auto highlight</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="Use arrow keys or type" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const tags = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" },];Grid
Enable grid and render rows manually when options should navigate as a grid.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteRow, AutocompleteItem, AutocompleteItemText, useAutocompleteFilteredItems,} from "moduix";import { useId } from "react";function ShortcutGrid() { const filteredItems = useAutocompleteFilteredItems(); if (filteredItems.length === 0) { return null; } return ( <AutocompleteList> {chunkArray(filteredItems, 6).map((row) => ( <AutocompleteRow key={row.map((item) => item.id).join("-")}> {row.map((item) => ( <AutocompleteItem key={item.id} value={item} aria-label={item.label}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> ))} </AutocompleteRow> ))} </AutocompleteList> );}export function GridAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={shortcuts} itemToStringValue={(item) => item.label} grid openOnInputClick > <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Shortcut command</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="Type a command" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No shortcuts found.</AutocompleteEmpty> <ShortcutGrid /> </AutocompleteContent> </Autocomplete> );}const shortcuts = [ { id: "s1", value: "N", label: "New file" }, { id: "s2", value: "O", label: "Open file" }, { id: "s3", value: "S", label: "Save file" }, { id: "s4", value: "P", label: "Print" }, { id: "s5", value: "F", label: "Find" }, { id: "s6", value: "R", label: "Replace" },];function chunkArray(items, size) { const chunks = []; for (let index = 0; index < items.length; index += size) { chunks.push(items.slice(index, index + size)); } return chunks;}Fuzzy
Provide a custom filter when matching should differ from the default string comparison.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function FuzzyAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={topMovies} itemToStringValue={(item) => item.title} filter={(item, query, itemToString) => { const label = itemToString ? itemToString(item) : String(item); return isFuzzyMatch(label, query); }} > <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Fuzzy search</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. tdk or sra" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteEmpty>No movies found.</AutocompleteEmpty> <AutocompleteList> {(movie) => ( <AutocompleteItem key={movie.id} value={movie}> <AutocompleteItemText> {movie.title} ({movie.year}) </AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const topMovies = [ { id: "1", title: "The Shawshank Redemption", year: 1994 }, { id: "2", title: "The Godfather", year: 1972 }, { id: "3", title: "The Dark Knight", year: 2008 }, { id: "4", title: "Pulp Fiction", year: 1994 },];function isFuzzyMatch(value, query) { const normalizedValue = value.toLowerCase().trim(); const normalizedQuery = query.toLowerCase().trim(); if (normalizedQuery === "") { return true; } let queryIndex = 0; for (const character of normalizedValue) { if (character === normalizedQuery[queryIndex]) { queryIndex += 1; if (queryIndex === normalizedQuery.length) { return true; } } } return false;}Async Search
Control value, disable the built-in filter with filter={null}, and keep AutocompleteStatus mounted while its children change.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteStatus, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText, useAutocompleteFilter,} from "moduix";import { useId, useRef, useState, useTransition } from "react";export function AsyncSearchAutocompleteDemo() { const id = useId(); const { contains } = useAutocompleteFilter(); const [value, setValue] = useState(""); const [searchResults, setSearchResults] = useState([]); const [isPending, startTransition] = useTransition(); const abortControllerRef = useRef(null); const trimmedValue = value.trim(); const status = isPending ? "Searching..." : trimmedValue !== "" && searchResults.length === 0 ? `No matches for "${trimmedValue}".` : null; return ( <Autocomplete items={searchResults} value={value} filter={null} itemToStringValue={(item) => item.title} onValueChange={(nextValue) => { setValue(nextValue); const controller = new AbortController(); abortControllerRef.current?.abort(); abortControllerRef.current = controller; if (nextValue.trim() === "") { setSearchResults([]); return; } startTransition(async () => { await new Promise((resolve) => setTimeout(resolve, 250)); if (controller.signal.aborted) { return; } setSearchResults( topMovies.filter( (movie) => contains(movie.title, nextValue) || contains(movie.year.toString(), nextValue), ), ); }); }} > <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}> Search movies by name or year </AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. Pulp Fiction or 1994" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent> <AutocompleteStatus>{status}</AutocompleteStatus> <AutocompleteEmpty> {trimmedValue !== "" && !isPending ? "Try a different query." : null} </AutocompleteEmpty> <AutocompleteList> {(movie) => ( <AutocompleteItem key={movie.id} value={movie}> <AutocompleteItemText> {movie.title} ({movie.year}) </AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}const topMovies = [ { id: "1", title: "The Shawshank Redemption", year: 1994 }, { id: "2", title: "The Godfather", year: 1972 }, { id: "3", title: "The Dark Knight", year: 2008 }, { id: "4", title: "Pulp Fiction", year: 1994 },];Custom Styles
Style the popup directly with className and service slots with classNames.
import { Autocomplete, AutocompleteField, AutocompleteFieldLabel, AutocompleteInputGroup, AutocompleteInput, AutocompleteControlActions, AutocompleteClear, AutocompleteTrigger, AutocompleteContent, AutocompleteEmpty, AutocompleteList, AutocompleteItem, AutocompleteItemText,} from "moduix";import { useId } from "react";export function CustomStylesAutocompleteDemo() { const id = useId(); return ( <Autocomplete items={tags} itemToStringValue={(item) => item.value}> <AutocompleteField> <AutocompleteFieldLabel htmlFor={id}>Search tags</AutocompleteFieldLabel> <AutocompleteInputGroup> <AutocompleteInput id={id} placeholder="e.g. feature" /> <AutocompleteControlActions> <AutocompleteClear aria-label="Clear value" /> <AutocompleteTrigger aria-label="Open suggestions" /> </AutocompleteControlActions> </AutocompleteInputGroup> </AutocompleteField> <AutocompleteContent className={styles.customPopup} sideOffset={8} withArrow withBackdrop classNames={{ portal: styles.customPortal, backdrop: styles.customBackdrop, positioner: styles.customPositioner, arrow: styles.customArrow, }} > <AutocompleteEmpty>No tags found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.id} value={item}> <AutocompleteItemText>{item.value}</AutocompleteItemText> </AutocompleteItem> )} </AutocompleteList> </AutocompleteContent> </Autocomplete> );}.customPortal { position: relative;}.customPositioner { --autocomplete-popup-max-height: 18rem;}.customPopup { --autocomplete-popup-bg: color-mix(in srgb, var(--color-popover) 92%, var(--color-primary)); --autocomplete-popup-border-color: color-mix(in srgb, var(--color-border) 72%, var(--color-primary)); --autocomplete-shadow: var(--shadow-md);}.customBackdrop { --autocomplete-backdrop-bg: var(--color-overlay); --autocomplete-backdrop-blur: 2px;}.customArrow { --autocomplete-arrow-stroke-color: var(--color-border);}const tags = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" },];