tsimport {XDSTypeahead} from '@xds/core/Typeahead'
| Guidance | Practices |
|---|---|
| Do | Provide descriptive placeholder text that hints at what users can search for. |
| Do | Show suggestions on focus when users benefit from seeing popular or recent options before typing. |
| Do | Add a search delay for remote data sources to avoid excessive network requests. |
| Don't | Use for short, static option lists — use Selector for better discoverability. |
| Don't | Use for multi-selection — use Tokenizer instead. |
| Don't | Place multiple Typeaheads adjacent to each other without clear labels differentiating them. |
| Prop | Type | Description |
|---|---|---|
searchSourcerequired | XDSSearchSource<T> | Data source providing search and bootstrap methods. |
valuerequired | T | null | Currently selected item. |
onChangerequired | (item: T | null) => void | Called when the selection changes. |
renderItem | (item: T) => ReactNode | Custom render function for dropdown items. |
placeholder | string (default: 'Search...') | Input placeholder text. |
hasEntriesOnFocus | boolean (default: false) | Show bootstrap results on focus before typing. |
maxMenuItems | number (default: 10) | Maximum dropdown items to display. |
emptySearchResultsText | string (default: 'No results found') | Text shown when search returns no results. |
isDisabled | boolean (default: false) | Whether the input is disabled. |
hasAutoFocus | boolean (default: false) | Auto-focus the input on mount. |
debounceMs | number (default: 150) | Debounce delay in ms before triggering search. Set to 0 for synchronous sources. |
anchorRef | RefObject<HTMLElement | null> | Ref to the anchor element for dropdown positioning. If not provided, the input itself is used. |
inputXStyle | StyleXStyles | Additional StyleX styles for the input element. |
onKeyDown | (e: React.KeyboardEvent<HTMLInputElement>) => void | Additional keydown handler called before internal keyboard navigation. Call e.preventDefault() to skip internal handling. |
onChangeQuery | (query: string) => void | Callback fired when the search query text changes. |
onOpenChange | (isOpen: boolean) => void | Callback when the dropdown opens or closes. |
inputId | string | ID for the input element (for label association). |
ariaDescribedBy | string | Additional aria-describedby IDs. |
| Prop | Type | Description |
|---|---|---|
labelrequired | string | Accessible label for the input. |
searchSourcerequired | XDSSearchSource<T> | Data source providing search and bootstrap methods for populating the dropdown. |
valuerequired | T | null | Currently selected item, or null if nothing is selected. |
onChangerequired | (item: T | null) => void | Called when the selection changes. |
placeholder | string | Input placeholder text. |
hasEntriesOnFocus | boolean (default: false) | Show bootstrap results on focus before typing. |
hasClear | boolean (default: true) | Show clear button to deselect the current value. |
isDisabled | boolean (default: false) | Disables the input. |
maxMenuItems | number (default: 10) | Maximum number of dropdown items to display. |
status | XDSInputStatus | Validation status object with type and message for error/warning/success states. |
renderItem | (item: T) => ReactNode | Custom render function for dropdown items. Default renders XDSTypeaheadItem. |
isLabelHidden | boolean (default: false) | Visually hides the label while keeping it accessible. |
description | string | Helper text displayed below the label. |
isRequired | boolean (default: false) | Marks the field as required. |
isOptional | boolean (default: false) | Shows an optional indicator on the label. |
labelTooltip | string | Tooltip text shown on the label. |
emptySearchResultsText | string (default: 'No results found') | Text shown when search returns no results. |
hasAutoFocus | boolean (default: false) | Auto-focus the input on mount. |
size | 'sm' | 'md' (default: 'md') | Input and token size. |
debounceMs | number (default: 150) | Debounce delay in ms before triggering search. Set to 0 for synchronous sources. |
onChangeQuery | (query: string) => void | Callback fired when the search query text changes. |
onOpenChange | (isOpen: boolean) => void | Callback when the dropdown opens or closes. |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
| Prop | Type | Description |
|---|---|---|
itemrequired | XDSSearchableItem | The search result item to render. |
icon | ReactNode | Icon or avatar to display before the label. |
description | string | Description text displayed below the label. |
isDisabled | boolean (default: false) | Whether this item is visually disabled. |
group | string | Group label for grouping items visually. |
tsx'use client';import {useState} from 'react';import {XDSTypeahead} from '@xds/core/Typeahead';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';import {XDSCenter} from '@xds/core/Center';const items: XDSSearchableItem[] = [{id: '1', label: 'United States'},{id: '2', label: 'United Kingdom'},{id: '3', label: 'Canada'},{id: '4', label: 'Australia'},{id: '5', label: 'Germany'},{id: '6', label: 'France'},{id: '7', label: 'Japan'},{id: '8', label: 'Brazil'},];const searchSource: XDSSearchSource = {search: (query: string) =>items.filter(i => i.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => items.slice(0, 5),};export default function TypeaheadLimitedResults() {const [value, setValue] = useState<XDSSearchableItem | null>(null);return (<XDSCenter width={320}><XDSTypeaheadlabel="Country"placeholder="Search countries..."searchSource={searchSource}value={value}onChange={setValue}hasEntriesOnFocusmaxMenuItems={3}/></XDSCenter>);}
tsx'use client';import {useState} from 'react';import {XDSTypeahead} from '@xds/core/Typeahead';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';import {XDSCenter} from '@xds/core/Center';import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';const items: XDSSearchableItem[] = [{id: '1', label: 'Olivia Martin'},{id: '2', label: 'Jackson Lee'},{id: '3', label: 'Isabella Nguyen'},{id: '4', label: 'William Kim'},{id: '5', label: 'Sofia Davis'},{id: '6', label: 'Lucas Brown'},{id: '7', label: 'Mia Wilson'},{id: '8', label: 'Ethan Jones'},];const searchSource: XDSSearchSource = {search: (query: string) =>items.filter(i => i.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => items.slice(0, 5),};export default function TypeaheadSearchField() {const [value, setValue] = useState<XDSSearchableItem | null>(null);return (<XDSCenter width={320}><XDSTypeaheadlabel="Team member"placeholder="Search people..."searchSource={searchSource}value={value}onChange={setValue}startIcon={MagnifyingGlassIcon}hasEntriesOnFocus/></XDSCenter>);}
tsx'use client';import {useState} from 'react';import {XDSTypeahead} from '@xds/core/Typeahead';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';import {XDSCenter} from '@xds/core/Center';const items: XDSSearchableItem[] = [{id: '1', label: 'Engineering'},{id: '2', label: 'Design'},{id: '3', label: 'Marketing'},{id: '4', label: 'Sales'},{id: '5', label: 'Product'},{id: '6', label: 'Finance'},{id: '7', label: 'Legal'},{id: '8', label: 'Operations'},];const searchSource: XDSSearchSource = {search: (query: string) =>items.filter(i => i.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => items.slice(0, 5),};export default function TypeaheadWithHelperText() {const [value, setValue] = useState<XDSSearchableItem | null>(null);return (<XDSCenter width={320}><XDSTypeaheadlabel="Department"placeholder="Search departments..."searchSource={searchSource}value={value}onChange={setValue}description="Select the department this request should be routed to"/></XDSCenter>);}
tsx'use client';import {useState} from 'react';import {XDSTypeahead} from '@xds/core/Typeahead';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';import {XDSCenter} from '@xds/core/Center';const items: XDSSearchableItem[] = [{id: '1', label: 'New York'},{id: '2', label: 'San Francisco'},{id: '3', label: 'London'},{id: '4', label: 'Berlin'},{id: '5', label: 'Tokyo'},{id: '6', label: 'Singapore'},{id: '7', label: 'Sydney'},{id: '8', label: 'Toronto'},];const searchSource: XDSSearchSource = {search: (query: string) =>items.filter(i => i.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => items.slice(0, 5),};export default function TypeaheadWithValidation() {const [value, setValue] = useState<XDSSearchableItem | null>(null);return (<XDSCenter width={320}><XDSTypeaheadlabel="Office"placeholder="Search offices..."searchSource={searchSource}value={value}onChange={setValue}status={{type: 'error', message: 'Please select an office location'}}/></XDSCenter>);}
tsximport {XDSTypeahead} from '@xds/core/Typeahead';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const fruits: XDSSearchableItem[] = [{id: '1', label: 'Apple'},{id: '2', label: 'Banana'},{id: '3', label: 'Cherry'},{id: '4', label: 'Date'},{id: '5', label: 'Elderberry'},{id: '6', label: 'Fig'},{id: '7', label: 'Grape'},{id: '8', label: 'Honeydew'},];const fruitSource: XDSSearchSource = {search: (query: string) =>fruits.filter(f => f.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => fruits.slice(0, 5),};export default function TypeaheadShowcase() {return (<div style={{width: 320}}><XDSTypeaheadlabel="Fruit"placeholder="Search fruits..."searchSource={fruitSource}value={null}onChange={() => {}}/></div>);}