Select
Professional dropdown selection component with search, multi-select, and custom styling options.
Select Variants
Standard Select
Basic dropdown selection
Option 1
Option 2
Option 3
Option 4
Select with Search
Filterable dropdown list
Germany
France
United States
United Kingdom
Japan
Australia
Disabled Options
Select with disabled items
Active
Pending (unavailable)
Completed
Archived (unavailable)
Select Sizes
Different size variants
Option 1
Option 1
Option 1
Multi-Select
Select multiple options with checkboxes
Parking
Gym
Swimming Pool
Balcony
Garden
Elevator
Multi-Select with Tags
Display selected items as removable tags
Real Estate Use Cases
Property Type
Apartment
House
Villa
Studio
Price Range
€0 - €500,000
€500,000 - €1,000,000
€1,000,000 - €2,000,000
€2,000,000+
Implementation Guide
HTML Structure
<!-- Standard Select -->
<div class="lyd-select">
<button class="lyd-select-trigger" >
<span>Choose an option</span>
<svg class="lyd-select-icon" width="20" height="20" viewBox="0 0 24 24">
<path d="M6 9l6 6 6-6" />
</svg>
</button>
<div class="lyd-select-dropdown">
<div class="lyd-select-option">Option 1</div>
<div class="lyd-select-option">Option 2</div>
<div class="lyd-select-option">Option 3</div>
</div>
</div>
<!-- Select with Search -->
<div class="lyd-select">
<button class="lyd-select-trigger">
<span>Select item</span>
<svg class="lyd-select-icon">...</svg>
</button>
<div class="lyd-select-dropdown">
<div class="lyd-select-search">
<input type="text" placeholder="Search...">
</div>
<div class="lyd-select-option">Item 1</div>
<div class="lyd-select-option">Item 2</div>
</div>
</div>
JavaScript for Dropdown Toggle
// Toggle dropdown
function toggleSelect(trigger) {
const dropdown = trigger.nextElementSibling;
const isOpen = trigger.classList.contains('active');
// Close all other dropdowns
document.querySelectorAll('.lyd-select-trigger').forEach(t => {
t.classList.remove('active');
t.nextElementSibling.classList.remove('show');
});
if (!isOpen) {
trigger.classList.add('active');
dropdown.classList.add('show');
}
}
// Select option
function selectOption(option, value) {
if (option.classList.contains('disabled')) return;
const select = option.closest('.lyd-select');
const trigger = select.querySelector('.lyd-select-trigger span');
const dropdown = select.querySelector('.lyd-select-dropdown');
// Update selected state
dropdown.querySelectorAll('.lyd-select-option').forEach(opt => {
opt.classList.remove('selected');
});
option.classList.add('selected');
// Update trigger text
trigger.textContent = value;
// Close dropdown
select.querySelector('.lyd-select-trigger').classList.remove('active');
dropdown.classList.remove('show');
}
// Filter options
function filterOptions(input) {
const filter = input.value.toLowerCase();
const options = input.closest('.lyd-select-dropdown')
.querySelectorAll('.lyd-select-option');
options.forEach(option => {
const text = option.textContent.toLowerCase();
option.style.display = text.includes(filter) ? '' : 'none';
});
}
// Close on outside click
document.addEventListener('click', (e) => {
if (!e.target.closest('.lyd-select')) {
document.querySelectorAll('.lyd-select-trigger').forEach(trigger => {
trigger.classList.remove('active');
trigger.nextElementSibling.classList.remove('show');
});
}
});
React/Next.js Component
// components/Select.tsx
import { useState, useRef, useEffect } from 'react';
interface Option {
value: string;
label: string;
disabled?: boolean;
}
interface SelectProps {
options: Option[];
placeholder?: string;
searchable?: boolean;
size?: 'small' | 'medium' | 'large';
value?: string;
onChange?: (value: string) => void;
}
export const Select = ({
options,
placeholder = 'Select an option',
searchable = false,
size = 'medium',
value,
onChange
}: SelectProps) => {
const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState('');
const [selected, setSelected] = useState(value);
const selectRef = useRef<HTMLDivElement>(null);
const filteredOptions = searchable
? options.filter(opt =>
opt.label.toLowerCase().includes(search.toLowerCase())
)
: options;
const handleSelect = (option: Option) => {
if (option.disabled) return;
setSelected(option.value);
setIsOpen(false);
setSearch('');
onChange?.(option.value);
};
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (selectRef.current && !selectRef.current.contains(e.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
return (
<div ref={selectRef} className={`lyd-select ${size}`}>
<button
className={`lyd-select-trigger ${isOpen ? 'active' : ''}`}
onClick={() => setIsOpen(!isOpen)}
>
<span>
{selected
? options.find(o => o.value === selected)?.label
: placeholder}
</span>
<svg className="lyd-select-icon" width="20" height="20">
<path d="M6 9l6 6 6-6" />
</svg>
</button>
<div className={`lyd-select-dropdown ${isOpen ? 'show' : ''}`}>
{searchable && (
<div className="lyd-select-search">
<input
type="text"
placeholder="Search..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
)}
{filteredOptions.map(option => (
<div
key={option.value}
className={`lyd-select-option ${
option.disabled ? 'disabled' : ''
} ${selected === option.value ? 'selected' : ''}`}
onClick={() => handleSelect(option)}
>
{option.label}
</div>
))}
</div>
</div>
);
};
API Reference
| Class | Description |
|---|---|
.lyd-select |
Main select container |
.lyd-select-trigger |
Clickable trigger button |
.lyd-select-dropdown |
Dropdown options container |
.lyd-select-option |
Individual option item |
.lyd-select-search |
Search input container |
.lyd-select-icon |
Dropdown arrow icon |
.small |
Small size variant |
.large |
Large size variant |
.active |
Active/open state |
.selected |
Selected option state |
.disabled |
Disabled option state |
.multi |
Multi-select variant |
Accessibility & Best Practices
Keyboard Navigation
- Enter/Space to open dropdown
- Arrow keys to navigate options
- Enter to select option
- Escape to close dropdown
Screen Reader Support
- Use aria-expanded for dropdown state
- Announce selected value
- Provide clear labels
- Role="listbox" for options
Best Practices
- Provide a default/placeholder
- Show selected state clearly
- Handle long option lists with scroll
- Consider search for 10+ options
Visual Feedback
- Clear hover states
- Focus indicators
- Disabled state styling
- Loading states when needed