import { Autocomplete } from "@cloudflare/kumo";
/** Basic autocomplete with a flat list of strings. */
export function AutocompleteDemo() {
return (
<Autocomplete items={fruits}>
<Autocomplete.InputGroup
aria-label="Search fruits"
placeholder="Search fruits…"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
);
} Installation
Barrel
import { Autocomplete } from "@cloudflare/kumo"; Granular
import { Autocomplete } from "@cloudflare/kumo/components/autocomplete"; When to use
Use Autocomplete when the input value can be free-form text and suggestions
are optional hints. Use Combobox instead when the selected value must come
from the predefined list.
Accessibility
Every autocomplete input needs an accessible name. Prefer the root label
prop for visible field labels. For compact or visually labelled layouts, pass
aria-label or aria-labelledby to Autocomplete.InputGroup. Placeholder
text is only a hint and does not count as the accessible name.
<Autocomplete items={countries} label="Country">
<Autocomplete.InputGroup placeholder="Search countries…" />
</Autocomplete>
<Autocomplete items={countries}>
<Autocomplete.InputGroup
aria-label="Search countries"
placeholder="Search countries…"
/>
</Autocomplete> Controlled
Pass value and onValueChange for controlled usage.
import { useState } from "react";
import { Autocomplete } from "@cloudflare/kumo";
/** Controlled autocomplete with value and onValueChange. */
export function AutocompleteControlledDemo() {
const [value, setValue] = useState("");
return (
<div className="flex flex-col gap-3 w-80">
<Autocomplete
items={fruits}
value={value}
onValueChange={(v) => setValue(v)}
>
<Autocomplete.InputGroup
aria-label="Type a fruit"
placeholder="Type a fruit…"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
{value && (
<p className="text-sm text-kumo-subtle">
Value: <span className="text-kumo-default font-medium">{value}</span>
</p>
)}
</div>
);
} With Field
Add label, description, and required to enable the built-in Field wrapper.
Start typing to filter languages
import { useCallback } from "react";
import { Autocomplete } from "@cloudflare/kumo";
import { languages, Language } from "./data/languages";
/** Autocomplete with label, description, and Field wrapper. */
export function AutocompleteWithFieldDemo() {
const { contains } = Autocomplete.useFilter();
const filter = useCallback(
(item: Language, query: string) => contains(item.label, query),
[contains],
);
return (
<div className="w-80">
<Autocomplete
items={languages}
label="Language"
description="Start typing to filter languages"
filter={filter}
>
<Autocomplete.InputGroup placeholder="Search a language…" />
<Autocomplete.Content>
<Autocomplete.List>
{(item: Language) => (
<Autocomplete.Item key={item.value} value={item}>
{item.emoji} {item.label}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
</div>
);
} Error State
Display validation errors with the error prop.
import { useCallback } from "react";
import { Autocomplete } from "@cloudflare/kumo";
/** Autocomplete with error state via the Field wrapper. */
export function AutocompleteErrorDemo() {
const { contains } = Autocomplete.useFilter();
const filter = useCallback(
(item: Country, query: string) => contains(item.label, query),
[contains],
);
return (
<div className="w-80">
<Autocomplete
items={countries}
label="Country"
error={{ message: "Please enter a valid country", match: true }}
filter={filter}
>
<Autocomplete.InputGroup placeholder="Search countries…" />
<Autocomplete.Content>
<Autocomplete.List>
{(item: Country) => (
<Autocomplete.Item key={item.code} value={item}>
{item.label}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
</div>
);
} Grouped
Group items into categories using Autocomplete.Group and Autocomplete.GroupLabel.
import { Autocomplete } from "@cloudflare/kumo";
/** Autocomplete with grouped items using Group and GroupLabel. */
export function AutocompleteGroupedDemo() {
return (
<Autocomplete items={servers}>
<Autocomplete.InputGroup
aria-label="Select server region"
placeholder="Select region…"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(group: ServerGroup) => (
<Autocomplete.Group key={group.value} items={group.items}>
<Autocomplete.GroupLabel>{group.value}</Autocomplete.GroupLabel>
<Autocomplete.Collection>
{(item: ServerLocation) => (
<Autocomplete.Item key={item.value} value={item}>
{item.label}
</Autocomplete.Item>
)}
</Autocomplete.Collection>
</Autocomplete.Group>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
);
} Sizes
The size prop on Autocomplete.InputGroup supports four variants matching the
Input component: xs, sm, base (default), and lg.
import { Autocomplete } from "@cloudflare/kumo";
/** Demonstrates the four size variants: xs, sm, base, and lg. */
export function AutocompleteSizesDemo() {
return (
<div className="flex flex-wrap items-center gap-4">
<Autocomplete items={fruits.slice(0, 10)}>
<Autocomplete.InputGroup
aria-label="Search fruits, extra small"
size="xs"
placeholder="xs"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
<Autocomplete items={fruits.slice(0, 10)}>
<Autocomplete.InputGroup
aria-label="Search fruits, small"
size="sm"
placeholder="sm"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
<Autocomplete items={fruits.slice(0, 10)}>
<Autocomplete.InputGroup
aria-label="Search fruits, default"
size="base"
placeholder="base (default)"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
<Autocomplete items={fruits.slice(0, 10)}>
<Autocomplete.InputGroup
aria-label="Search fruits, large"
size="lg"
placeholder="lg"
/>
<Autocomplete.Content>
<Autocomplete.List>
{(item: string) => (
<Autocomplete.Item key={item} value={item}>
{item}
</Autocomplete.Item>
)}
</Autocomplete.List>
</Autocomplete.Content>
</Autocomplete>
</div>
);
} Filtering
Filtering is case- and accent-insensitive by default, powered by
Intl.Collator under the hood. For string items, no custom filter is
needed.
When filtering on a property of object items, use
Autocomplete.useFilter() to preserve the built-in accent-insensitive
matching:
function LanguagePicker() {
const { contains } = Autocomplete.useFilter();
const filter = useCallback(
(item: Language, query: string) => contains(item.label, query),
[contains],
);
return (
<Autocomplete items={languages} label="Language" filter={filter}>
{/* ... */}
</Autocomplete>
);
}To disable filtering entirely (e.g. when results come from a server), pass
filter={null}:
<Autocomplete items={results} label="Search results" filter={null}>
...
</Autocomplete> API Reference
Autocomplete
Root component. Wraps all sub-components and manages state.
| Prop | Type | Default | Description |
|---|---|---|---|
| items* | unknown[] | - | Array of items to display in the dropdown |
| value | string | number | string[] | - | The controlled input value |
| open | boolean | - | Whether the popup is open (controlled) |
| children | ReactNode | - | Autocomplete content (input group, popup content) |
| className | string | - | Additional CSS classes |
| label | ReactNode | - | Label content (enables Field wrapper) |
| required | boolean | - | Whether the field is required |
| labelTooltip | ReactNode | - | Tooltip content to display next to the label |
| description | ReactNode | - | Helper text displayed below the field |
| error | string | object | - | Error message or validation error object |
Autocomplete.InputGroup
Styled text input. Provide its accessible name with the root label, or pass
aria-label / aria-labelledby here when there is no visible label.
| Prop | Type | Default |
|---|---|---|
| className | string | - |
| size | KumoAutocompleteSize | - |
| placeholder | string | - |
Autocomplete.Content
Dropdown popup container. Wraps Portal, Positioner, and Popup.
| Prop | Type | Default |
|---|---|---|
| children | ReactNode | - |
| className | string | - |
| align | AutocompleteBase.Positioner.Props["align"] | - |
| alignOffset | AutocompleteBase.Positioner.Props["alignOffset"] | - |
| side | AutocompleteBase.Positioner.Props["side"] | - |
| sideOffset | AutocompleteBase.Positioner.Props["sideOffset"] | - |
Autocomplete.Item
Individual suggestion item in the list.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
Additional Sub-components
Autocomplete.List— scrollable list container with render propAutocomplete.Group— groups items under a headingAutocomplete.GroupLabel— heading label for a groupAutocomplete.Collection— item container within a groupAutocomplete.Separator— horizontal divider between items