AutoComplete
Provide search functionality with suggestions, supporting single/multi-select, async data loading, and rich content display.
Source
LLM docs
Package
Examples
10
Properties
98
Playground
Basics
Simple auto-complete. Start typing to filter the list. Selecting an option fills the input.
Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const sports = [
{ label: 'Football', value: 'football' },
{ label: 'Basketball', value: 'basketball' },
{ label: 'Soccer', value: 'soccer' },
{ label: 'Baseball', value: 'baseball' },
{ label: 'Tennis', value: 'tennis' },
{ label: 'Golf', value: 'golf' },
{ label: 'Swimming', value: 'swimming' },
{ label: 'Volleyball', value: 'volleyball' },
{ label: 'Cricket', value: 'cricket' },
{ label: 'Rugby', value: 'rugby' },
{ label: 'Softball', value: 'softball' },
{ label: 'Hockey', value: 'hockey' },
];
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Choose a sport"
placeholder="Search for a sport..."
data={sports}
minSearchLength={1}
fullWidth
/>
</Block>
);
}Multi-select tags
Tap an item to add or remove it. Selected genres render as removable chips.
Loading demo…
import { useState } from 'react';
import { AutoComplete, Block } from '@plocks/ui';
import type { AutoCompleteOption } from '@plocks/ui';
const genres = [
{ label: 'Pop', value: 'pop' },
{ label: 'Rock', value: 'rock' },
{ label: 'Hip Hop', value: 'hiphop' },
{ label: 'Jazz', value: 'jazz' },
{ label: 'Classical', value: 'classical' },
{ label: 'Electronic', value: 'electronic' },
{ label: 'Country', value: 'country' },
{ label: 'R&B', value: 'rnb' },
];
export function Demo() {
const [selectedGenres, setSelectedGenres] = useState<AutoCompleteOption[]>([]);
const handleToggle = (option: AutoCompleteOption) => {
const isSelected = selectedGenres.some((genre) => genre.value === option.value);
setSelectedGenres((current) =>
isSelected
? current.filter((genre) => genre.value !== option.value)
: [...current, option],
);
};
return (
<Block fullWidth>
<AutoComplete
label="Music genres"
placeholder="Search genres..."
data={genres}
onSelect={handleToggle}
multiSelect
selectedValues={selectedGenres}
minSearchLength={0}
fullWidth
/>
</Block>
);
}Variants
Compare the default, filled, outline, and unstyled field shells on AutoComplete.
Loading demo…
import { Column, AutoComplete } from '@plocks/ui';
const options = ['Apple', 'Banana', 'Cherry'].map(value => ({ label: value, value: value.toLowerCase() }));
const variants = ['default', 'filled', 'outline', 'unstyled'] as const;
export function Demo() {
return (
<Column gap="md" fullWidth>
{variants.map(variant => (
<AutoComplete key={variant} variant={variant} label={`${variant} variant`} data={options} placeholder="Choose a fruit" />
))}
</Column>
);
}Select-Like Behavior
editable={false}), so it can't be typed into or filtered. Tapping opens the full option list (filter={() => true}) and the value is chosen from it.Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const countries = [
{ label: 'United States', value: 'us' },
{ label: 'Canada', value: 'ca' },
{ label: 'United Kingdom', value: 'uk' },
{ label: 'Germany', value: 'de' },
{ label: 'France', value: 'fr' },
{ label: 'Italy', value: 'it' },
{ label: 'Spain', value: 'es' },
{ label: 'Netherlands', value: 'nl' },
{ label: 'Australia', value: 'au' },
{ label: 'Japan', value: 'jp' },
{ label: 'South Korea', value: 'kr' },
{ label: 'Brazil', value: 'br' },
{ label: 'Mexico', value: 'mx' },
{ label: 'India', value: 'in' },
{ label: 'China', value: 'cn' },
];
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Country"
placeholder="Select a country..."
data={countries}
maxSuggestions={countries.length}
editable={false}
caretHidden
filter={() => true}
highlightMatches={false}
fullWidth
/>
</Block>
);
}Async auto-complete
Performs a debounced search against a simulated API before returning matches.
Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const languages = [
{ label: 'JavaScript', value: 'javascript' },
{ label: 'TypeScript', value: 'typescript' },
{ label: 'Python', value: 'python' },
{ label: 'Java', value: 'java' },
{ label: 'C++', value: 'cpp' },
{ label: 'C#', value: 'csharp' },
{ label: 'Go', value: 'go' },
{ label: 'Rust', value: 'rust' },
{ label: 'Swift', value: 'swift' },
{ label: 'Kotlin', value: 'kotlin' },
];
const searchLanguages = async (query: string) => {
await new Promise((resolve) => setTimeout(resolve, 400));
const normalized = query.toLowerCase();
return languages.filter((language) => language.label.toLowerCase().includes(normalized));
};
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Search programming languages"
placeholder="Start typing..."
onSearch={searchLanguages}
fullWidth
/>
</Block>
);
}Free Solo
Suggests fruits while still accepting custom values.
Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const fruits = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Grape', value: 'grape' },
{ label: 'Mango', value: 'mango' },
{ label: 'Pineapple', value: 'pineapple' },
];
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Favorite fruit"
placeholder="Type anything..."
data={fruits}
freeSolo
minSearchLength={0}
fullWidth
/>
</Block>
);
}Free Solo (multi-select)
Suggests fruits but lets you add any custom value as a tag — press Enter to add what you typed.
Loading demo…
import { useState } from 'react';
import { AutoComplete, Block } from '@plocks/ui';
import type { AutoCompleteOption } from '@plocks/ui';
const fruits = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Orange', value: 'orange' },
{ label: 'Grape', value: 'grape' },
{ label: 'Mango', value: 'mango' },
{ label: 'Pineapple', value: 'pineapple' },
];
export function Demo() {
const [selected, setSelected] = useState<AutoCompleteOption[]>([]);
const handleToggle = (option: AutoCompleteOption) => {
const isSelected = selected.some((item) => item.value === option.value);
setSelected((current) =>
isSelected
? current.filter((item) => item.value !== option.value)
: [...current, option],
);
};
return (
<Block fullWidth>
<AutoComplete
label="Favorite fruits"
placeholder="Type a fruit and press Enter..."
data={fruits}
onSelect={handleToggle}
freeSolo
multiSelect
selectedValues={selected}
minSearchLength={0}
fullWidth
/>
</Block>
);
}Grouped suggestions
Countries are organized by region to make large lists easier to scan.
Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const countries = [
{ label: 'United States', value: 'us', group: 'North America' },
{ label: 'Canada', value: 'ca', group: 'North America' },
{ label: 'Mexico', value: 'mx', group: 'North America' },
{ label: 'United Kingdom', value: 'uk', group: 'Europe' },
{ label: 'Germany', value: 'de', group: 'Europe' },
{ label: 'France', value: 'fr', group: 'Europe' },
{ label: 'Japan', value: 'jp', group: 'Asia' },
{ label: 'India', value: 'in', group: 'Asia' },
{ label: 'Australia', value: 'au', group: 'Oceania' },
{ label: 'Brazil', value: 'br', group: 'South America' },
];
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Search countries"
placeholder="Search for a country..."
data={countries}
minSearchLength={1}
fullWidth
/>
</Block>
);
}Rich Content
renderItem to lay out each suggestion and renderValue to draw the chosen option inside the field; refocusAfterSelect={false} blurs the field on select so that value shows right away.Loading demo…
import { AutoComplete, Block, Column, Icon, MenuItemButton, Row, Text } from '@plocks/ui';
interface RichSportOption {
label: string;
value: string;
emoji: string;
color: string;
price: number;
duration: string;
}
const sports: RichSportOption[] = [
{ label: 'Soccer', value: 'soccer', emoji: '⚽', color: '#22c55e', price: 75.5, duration: '90 min' },
{ label: 'Basketball', value: 'basketball', emoji: '🏀', color: '#f97316', price: 120.0, duration: '48 min' },
{ label: 'Football', value: 'football', emoji: '🏈', color: '#92400e', price: 180.0, duration: '60 min' },
{ label: 'Volleyball', value: 'volleyball', emoji: '🏐', color: '#fbbf24', price: 60.0, duration: 'Best of 5' },
{ label: 'Baseball', value: 'baseball', emoji: '⚾', color: '#ef4444', price: 85.0, duration: '9 innings' },
{ label: 'Golf', value: 'golf', emoji: '⛳', color: '#15803d', price: 110.0, duration: '4 hrs' },
];
const tint = (hex: string, alpha: string) => `${hex}${alpha}`;
const renderTile = (sport: RichSportOption, size: number) => (
<Block
w={size}
h={size}
radius="lg"
align="center"
justify="center"
bg={tint(sport.color, '26')}
borderWidth={1}
borderColor={tint(sport.color, '59')}
>
<Text size={size >= 40 ? 'xl' : 'md'}>{sport.emoji}</Text>
</Block>
);
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Search sports"
placeholder="Search sports..."
data={sports}
refocusAfterSelect={false}
renderItem={(item, _index, helpers) => {
const sport = item as RichSportOption;
return (
<MenuItemButton
rounded={false}
compact
fullWidth
active={helpers.isHighlighted || helpers.isSelected}
onPress={() => helpers.onSelect(sport)}
style={{ alignItems: 'stretch', gap: 0 }}
>
<Row align="center" gap="md" px="md" py="sm" fullWidth>
{renderTile(sport, 40)}
<Column grow={1} gap="xs">
<Text size="sm" fw="semibold" numberOfLines={1}>
{sport.label}
</Text>
<Text size="xs" c="secondary" numberOfLines={1}>
{sport.duration}
</Text>
</Column>
<Column align="flex-end" gap="xs">
<Text size="sm" fw="semibold">
${sport.price.toFixed(2)}
</Text>
<Text size="xs" c="secondary">
avg ticket
</Text>
</Column>
{helpers.isSelected ? (
<Icon name="check" size={16} stroke={3} color={sport.color} />
) : (
<Block w={16} />
)}
</Row>
</MenuItemButton>
);
}}
renderValue={(item) => {
const sport = item as RichSportOption;
return (
<Row align="center" gap="sm" grow={1}>
{renderTile(sport, 24)}
<Text size="sm" fw="semibold">{sport.label}</Text>
<Text size="xs" c="secondary">
{sport.duration}
</Text>
<Block grow={1} />
<Text size="sm" fw="semibold">
${sport.price.toFixed(2)}
</Text>
</Row>
);
}}
minSearchLength={1}
fullWidth
/>
</Block>
);
}Highlight colours
highlightMatches bolds and tints the part of each suggestion that matches what you typed. Pass highlightColor (a CSS color or a palette shade such as 'highlight.8') to change that tint, and optionally highlightBackgroundColor to fill behind it.Loading demo…
import { AutoComplete, Block } from '@plocks/ui';
const fruits = [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Cherry', value: 'cherry' },
{ label: 'Date', value: 'date' },
{ label: 'Elderberry', value: 'elderberry' },
{ label: 'Fig', value: 'fig' },
{ label: 'Grape', value: 'grape' },
{ label: 'Honeydew', value: 'honeydew' },
];
export function Demo() {
return (
<Block fullWidth>
<AutoComplete
label="Search fruits"
placeholder="Type to search fruits..."
data={fruits}
highlightColor="highlight.8"
minSearchLength={0}
fullWidth
/>
</Block>
);
}React Native design for iOS, Android, and Web.
Quick Links
Resources