Autocomplete
Free text with suggestions, where any value is valid and suggestions help, such as addresses: Base UI's Autocomplete, inside a Field. Site and collection search compose it in Search.
import { Autocomplete } from '@fairgarden-private/design/components/Autocomplete'
Suggestions are { value, label? } objects, or titled groups; a chosen suggestion only fills the input, and Enter submits the typed text. There is no ✓, because nothing stays selected. When the server filters the suggestions, pass filter={null} and the current items.
Suggestions and states
Suggestions and states
No row is highlighted until the arrow keys move; the highlighted row takes the --primary4 soft fill and the --ds-stroke-3 start bar, and matches are marked by weight. loading shows "Searching…" in a polite live region; error shows the danger glyph and a message instead of silently closing; the empty row keeps what the user typed.
Pick a suggestion or keep typing.
'use client'
import * as React from 'react'
import {
Autocomplete,
type AutocompleteOption,
} from '@fairgarden-private/design/components/Autocomplete'
import { Field, FieldDescription, FieldLabel } from '@fairgarden-private/design/components/Field'
import styles from './states.module.css'
const streets: AutocompleteOption[] = [
{ value: '12 Marsh Lane' },
{ value: '14 Marsh Lane' },
{ value: '3 Heron Close' },
{ value: '27 Heron Way' },
{ value: '9 Osprey Road' },
{ value: '41 Tern Street' },
]
const places = [
{ label: 'Towns', items: [{ value: 'Ashby' }, { value: 'Bexley' }, { value: 'Carrow' }] },
{ label: 'Parks', items: [{ value: 'Ashby Common' }, { value: 'Bexley Woods' }] },
]
/**
* Free text with suggestions: any value is valid. Then a loading field
* (the "Searching…" row), a fetch failure and a disabled field.
*/
export function AutocompleteStates() {
const [loading, setLoading] = React.useState(false)
return (
<div className={styles.stack}>
<Field>
<FieldLabel>Street Address</FieldLabel>
<Autocomplete items={streets} placeholder="Start with the number…" />
<FieldDescription>Pick a suggestion or keep typing.</FieldDescription>
</Field>
<Field>
<FieldLabel>Meeting Place</FieldLabel>
<Autocomplete items={places} icon="search" showTrigger placeholder="Town or park…" />
</Field>
<Field>
<FieldLabel>Nearest Station</FieldLabel>
<Autocomplete
items={[]}
filter={null}
loading={loading}
onValueChange={(value) => setLoading(value.trim() !== '')}
placeholder="Type to search…"
/>
</Field>
<Field>
<FieldLabel>Parish</FieldLabel>
<Autocomplete
items={[]}
filter={null}
error="Couldn't load suggestions. Keep typing or try again."
placeholder="Type to search…"
/>
</Field>
<Field disabled>
<FieldLabel>County</FieldLabel>
<Autocomplete items={streets} defaultValue="Heron County" />
</Field>
</div>
)
}
Primary and secondary
Primary and secondary
primary sets the box, value and icons; secondary is unused at rest and becomes the danger scale while invalid.
'use client'
import {
Autocomplete,
type AutocompleteOption,
} from '@fairgarden-private/design/components/Autocomplete'
import { Field, FieldLabel } from '@fairgarden-private/design/components/Field'
import styles from './color.module.css'
const topics: AutocompleteOption[] = [
{ value: 'Birding' },
{ value: 'Botany' },
{ value: 'Bouldering' },
{ value: 'Butterflies' },
]
/** `primary` recolors the box and its icons; the popup keeps the white scope's defaults. */
export function AutocompleteColor() {
return (
<div className={styles.stack}>
<Field>
<FieldLabel>Primary Plum</FieldLabel>
<Autocomplete primary="plum" items={topics} icon="search" placeholder="Topic…" />
</Field>
<Field>
<FieldLabel>Primary Indigo</FieldLabel>
<Autocomplete primary="indigo" items={topics} placeholder="Topic…" />
</Field>
</div>
)
}
On grounds
On paper and forest
The box follows its ground; the popup renders in a portal as the white scope.
paper
forest
'use client'
import {
Autocomplete,
type AutocompleteOption,
} from '@fairgarden-private/design/components/Autocomplete'
import { Field, FieldLabel } from '@fairgarden-private/design/components/Field'
import { PresetGround } from '@/components/PresetGround'
import styles from './grounds.module.css'
const rivers: AutocompleteOption[] = [
{ value: 'Avon' },
{ value: 'Derwent' },
{ value: 'Severn' },
{ value: 'Tweed' },
]
const presets = ['paper', 'forest'] as const
/** The box follows its ground; the suggestion popup is always the white scope. */
export function AutocompleteGrounds() {
return (
<div className={styles.row}>
{presets.map((preset) => (
<PresetGround key={preset} preset={preset} className={styles.face}>
<p className={styles.name}>{preset}</p>
<Field>
<FieldLabel>River</FieldLabel>
<Autocomplete items={rivers} icon="search" placeholder="River name…" />
</Field>
</PresetGround>
))}
</div>
)
}
API Reference
A Base UI Autocomplete inside a Field. The input keeps any text: a
suggestion only fills it, and Enter submits the typed text. No row is
highlighted until the arrow keys move.
| Prop | Type | Description |
|---|---|---|
aria-label | | Names the input when no visible label exists. Prefer a |
clearLabel | | The clear בs accessible name. Default “Clear”. |
emptyText | | The empty row. Default “No matches for ‘[query]'". Keep what the user typed. |
error | | A fetch failure, shown with the danger glyph instead of silently closing. |
icon | | A leading icon in the box, such as |
items | | The suggestions, or titled groups. Pass |
loading | | Shows the loading row (“Searching…") while suggestions load. |
loadingText | | The loading row’s words. Default “Searching…". |
placeholder | | The input’s placeholder, ending in “…"; never the label. |
plate | | The box face becomes a nested |
primary | | Primary Radix scale: edge, value, icons and focus ring. Never defaulted [D133]. |
secondary | | Secondary Radix scale. Unused at rest; the danger scale while invalid. |
showTrigger | | Shows the chevron that opens the full list. Default |
triggerLabel | | The chevron’s accessible name. Default “Show suggestions”. |
className | | Class names for the box, added after the module’s own. |
autocomplete
type autocomplete = autocompleteAutocompleteOption
One suggestion: the text it puts in the input, and optional display words.
type AutocompleteOption = {
/** The text the suggestion fills in. */
value: string;
/** The words shown in the list. Default: `value`. */
label?: string;
disabled?: boolean;
}AutocompleteOptionGroup
A titled group of suggestions.
type AutocompleteOptionGroup = {
/** The group label, in `type-label` caps. */
label: string;
items: AutocompleteOption[];
}AutocompleteProps
Props for Autocomplete: Base UI Autocomplete.Root props plus the suggestions, rows and color axes.
type AutocompleteProps = {
/** The suggestions, or titled groups. Pass `filter={null}` when the server filters them. */
items?: AutocompleteItems;
/** The input's placeholder, ending in "…"; never the label. */
placeholder?: string;
/** A leading icon in the box, such as `search` (inline tier). */
icon?:
| 'search'
| 'arrow_forward'
| 'arrow_upward'
| 'expand_more'
| 'close'
| 'remove'
| 'add'
| 'check'
| 'circle'
| 'chevron_right'
| 'chevron_left'
| 'menu'
| 'more_horiz'
| 'play_arrow'
| 'pause'
| 'download'
| 'zoom_in'
| 'zoom_out'
| 'recenter'
| 'help'
| 'mail';
/** Shows the chevron that opens the full list. Default `false`. */
showTrigger?: boolean;
/** The empty row. Default "No matches for '[query]'". Keep what the user typed. */
emptyText?: (query: string) => React.ReactNode;
/** Shows the loading row ("Searching…") while suggestions load. */
loading?: boolean;
/** The loading row's words. Default "Searching…". */
loadingText?: React.ReactNode;
/** A fetch failure, shown with the danger glyph instead of silently closing. */
error?: React.ReactNode;
/** The clear ×'s accessible name. Default "Clear". */
clearLabel?: string;
/** The chevron's accessible name. Default "Show suggestions". */
triggerLabel?: string;
/** Names the input when no visible label exists. Prefer a `FieldLabel`. */
'aria-label'?: string;
/** Class names for the box, added after the module's own. */
className?: string;
/** The box face becomes a nested `white` scope; patterned grounds only (§10.1). */
plate?: boolean;
/** Primary Radix scale: edge, value, icons and focus ring. Never defaulted [D133]. */
primary?:
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null;
/** Secondary Radix scale. Unused at rest; the danger scale while invalid. */
secondary?:
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'ruby'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null;
}Specification: DESIGN-SYSTEM.md §10.6 (combobox and autocomplete).