Autocomplete
Intelligent search input with suggestions for locations, properties, and more.
Basic Autocomplete
Location Search
Munich
Bavaria, Germany
Munster
North Rhine-Westphalia, Germany
Munich Airport
Freising, Bavaria
Empty State
No results found for "xyz"
Loading State
Grouped Results
Property Search with Categories
Properties
Villa Monaco
€2,500,000 • 5 bedrooms
Villa Sunset
€1,800,000 • 4 bedrooms
Locations
Villach
Austria
Agents
John Villanueva
Senior Agent
Multi-Select Autocomplete
Property Features
Pool
Garden
Garage
Implementation Guide
HTML Structure
<!-- Autocomplete -->
<div class="lyd-autocomplete">
<input type="text" class="lyd-autocomplete-input"
placeholder="Type to search...">
<svg class="lyd-autocomplete-icon">...</svg>
<div class="lyd-autocomplete-dropdown">
<!-- Grouped Results -->
<div class="lyd-autocomplete-group">Properties</div>
<div class="lyd-autocomplete-item">
<svg class="lyd-autocomplete-item-icon">...</svg>
<div class="lyd-autocomplete-item-text">
<span class="lyd-autocomplete-item-primary">
<span class="lyd-autocomplete-highlight">Mun</span>ich
</span>
<span class="lyd-autocomplete-item-secondary">
Bavaria, Germany
</span>
</div>
</div>
</div>
</div>
React/Next.js Component
// components/Autocomplete.tsx
import { useState, useRef, useEffect } from 'react';
interface AutocompleteOption {
id: string;
label: string;
sublabel?: string;
group?: string;
icon?: React.ReactNode;
}
interface AutocompleteProps {
options: AutocompleteOption[];
placeholder?: string;
value?: string;
onChange?: (value: string) => void;
onSelect?: (option: AutocompleteOption) => void;
loading?: boolean;
multiSelect?: boolean;
}
export const Autocomplete = ({
options,
placeholder = 'Type to search...',
value = '',
onChange,
onSelect,
loading = false,
multiSelect = false
}: AutocompleteProps) => {
const [isOpen, setIsOpen] = useState(false);
const [searchValue, setSearchValue] = useState(value);
const [selectedOptions, setSelectedOptions] = useState([]);
const [activeIndex, setActiveIndex] = useState(0);
const ref = useRef(null);
const filteredOptions = options.filter(option =>
option.label.toLowerCase().includes(searchValue.toLowerCase())
);
const groupedOptions = filteredOptions.reduce((acc, option) => {
const group = option.group || 'Results';
if (!acc[group]) acc[group] = [];
acc[group].push(option);
return acc;
}, {} as Record);
const handleSelect = (option: AutocompleteOption) => {
if (multiSelect) {
setSelectedOptions([...selectedOptions, option]);
setSearchValue('');
} else {
setSearchValue(option.label);
onSelect?.(option);
}
setIsOpen(false);
};
const highlightMatch = (text: string) => {
const parts = text.split(new RegExp(`(${searchValue})`, 'gi'));
return parts.map((part, i) =>
part.toLowerCase() === searchValue.toLowerCase() ?
<span key={i} className="lyd-autocomplete-highlight">{part}</span> :
part
);
};
return (
<div className="lyd-autocomplete" ref={ref}>
{multiSelect && selectedOptions.length > 0 && (
<div className="lyd-autocomplete-chips">
{selectedOptions.map(option => (
<div key={option.id} className="lyd-autocomplete-chip">
{option.label}
<svg
className="lyd-autocomplete-chip-remove"
onClick={() => /* remove chip */}
>...</svg>
</div>
))}
</div>
)}
<input
type="text"
className="lyd-autocomplete-input"
placeholder={placeholder}
value={searchValue}
onChange={(e) => {
setSearchValue(e.target.value);
onChange?.(e.target.value);
setIsOpen(true);
}}
onFocus={() => setIsOpen(true)}
/>
{isOpen && (
<div className="lyd-autocomplete-dropdown open">
{loading ? (
<div className="lyd-autocomplete-loading">
<div className="lyd-autocomplete-spinner" />
</div>
) : filteredOptions.length === 0 ? (
<div className="lyd-autocomplete-empty">
No results found
</div>
) : (
Object.entries(groupedOptions).map(([group, items]) => (
<div key={group}>
{Object.keys(groupedOptions).length > 1 && (
<div className="lyd-autocomplete-group">{group}</div>
)}
{items.map((option, index) => (
<div
key={option.id}
className={`lyd-autocomplete-item ${
index === activeIndex ? 'active' : ''
}`}
onClick={() => handleSelect(option)}
>
{option.icon}
<div className="lyd-autocomplete-item-text">
<span className="lyd-autocomplete-item-primary">
{highlightMatch(option.label)}
</span>
{option.sublabel && (
<span className="lyd-autocomplete-item-secondary">
{option.sublabel}
</span>
)}
</div>
</div>
))}
</div>
))
)}
</div>
)}
</div>
);
};
API Reference
| Class | Description |
|---|---|
.lyd-autocomplete |
Container for autocomplete |
.lyd-autocomplete-input |
Search input field |
.lyd-autocomplete-dropdown |
Dropdown container |
.lyd-autocomplete-item |
Individual result item |
.lyd-autocomplete-group |
Group header |
.lyd-autocomplete-highlight |
Highlighted search match |
.lyd-autocomplete-chips |
Multi-select chips container |
.lyd-autocomplete-chip |
Selected item chip |
.active |
Active/focused item |
.selected |
Selected item |