Selector
A versatile dropdown selector component with support for search, multiple selection modes, and various item variants. Built on Radix UI Popover primitives for accessibility.
Basic Usage
import { Selector } from '@/components/Selector'
<Selector.Root value={value} onValueChange={setValue}>
<Selector.Trigger asChild>
<button>Select option...</button>
</Selector.Trigger>
<Selector.Content>
<Selector.Search placeholder="Search..." />
<Selector.Group>
<Selector.Item value="design">Design</Selector.Item>
<Selector.Item value="dev">Development</Selector.Item>
</Selector.Group>
<Selector.AddButton>Add new</Selector.AddButton>
</Selector.Content>
</Selector.Root>Multi-Select
Enable multi-selection mode with the multiple prop. Use showCheck to display checkmarks on selected items.
Selected: design, development
const [values, setValues] = useState(['design', 'development'])
<Selector.Root multiple showCheck value={values} onValueChange={setValues}>
<Selector.Trigger asChild>
<button>{values.length} selected</button>
</Selector.Trigger>
<Selector.Content>
<Selector.Group>
<Selector.Item value="design">Design</Selector.Item>
<Selector.Item value="development">Development</Selector.Item>
</Selector.Group>
</Selector.Content>
</Selector.Root>Item Variants
Selector supports six different item variants for various use cases.
Icon Variant
Items with icons for action menus.
<Selector.Item value="user" variant="icon" icon={<UserIcon />}>
User
</Selector.Item>User Variant
Items with avatars and secondary text for user selection.
<Selector.Item
value="user1"
variant="user"
avatar={<Avatar />}
secondaryText="@username"
>
User Name
</Selector.Item>Country Variant
Items with flags for country/currency selection.
<Selector.Item value="nok" variant="country" icon={<FlagNO />}>
Norwegian Krone (NOK)
</Selector.Item>Phone Variant
Items with flags and country names for phone number input.
<Selector.Item
value="us"
variant="phone"
icon={<FlagUS />}
secondaryText="United States"
>
+1
</Selector.Item>Company Variant
Items with logos for company/workspace selection.
<Selector.Item value="notion" variant="company" icon={<NotionLogo />}>
Notion HQ
</Selector.Item>All Variants Overview
Text (Default)
Icon
User
Country
Phone
Company
Without Search
The search input is optional - simply omit Selector.Search for simpler dropdowns.
<Selector.Content>
<Selector.Group>
<Selector.Item value="option1">Option 1</Selector.Item>
<Selector.Item value="option2">Option 2</Selector.Item>
</Selector.Group>
</Selector.Content>Disabled
Items can be disabled individually or the entire selector can be disabled.
Entire selector disabled
Individual items disabled
// Disable entire selector
<Selector.Root disabled>...</Selector.Root>
// Disable individual items
<Selector.Item value="logout" disabled>Log out</Selector.Item>Controlled
Control the selection state and open state externally.
Value: design | Open: No
const [value, setValue] = useState('design')
const [open, setOpen] = useState(false)
<Selector.Root
value={value}
onValueChange={setValue}
open={open}
onOpenChange={setOpen}
>
...
</Selector.Root>Accessibility
- Built on Radix UI Popover primitive for robust accessibility
- Full keyboard support (Tab to navigate, Enter/Space to select, Escape to close)
- Proper ARIA attributes for screen readers
- Focus management within the dropdown
- Search input filters items in real-time
API Reference
Selector.Root Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | string[] | - | The controlled value |
| defaultValue | string | string[] | - | The default value (uncontrolled) |
| onValueChange | (value: string | string[]) => void | - | Callback when value changes |
| multiple | boolean | false | Enable multi-selection mode |
| showCheck | boolean | false | Show checkmarks on selected items |
| disabled | boolean | false | Disable the selector |
| open | boolean | - | Controlled open state |
| defaultOpen | boolean | false | Default open state |
| onOpenChange | (open: boolean) => void | - | Callback when open state changes |
Selector.Trigger Props
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Render as child element |
| className | string | - | Additional CSS classes |
Selector.Content Props
| Prop | Type | Default | Description |
|---|---|---|---|
| sideOffset | number | 4 | Distance from trigger |
| align | 'start' | 'center' | 'end' | 'start' | Alignment relative to trigger |
| className | string | - | Additional CSS classes |
Selector.Search Props
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | 'Search...' | Placeholder text |
| className | string | - | Additional CSS classes |
Selector.Item Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | Required | Unique value for this item |
| variant | 'text' | 'icon' | 'user' | 'country' | 'phone' | 'company' | 'text' | Item display variant |
| icon | React.ReactNode | - | Icon element (for icon, country, phone, company variants) |
| avatar | React.ReactNode | - | Avatar element (for user variant) |
| secondaryText | string | - | Secondary text (for user, phone variants) |
| disabled | boolean | false | Disable this item |
| forceCheck | boolean | - | Override showCheck for this item |
| className | string | - | Additional CSS classes |
Selector.Group Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | - | Accessible label for the group |
| className | string | - | Additional CSS classes |
Selector.AddButton Props
| Prop | Type | Default | Description |
|---|---|---|---|
| icon | React.ReactNode | Plus icon | Custom icon |
| onClick | () => void | - | Click handler |
| className | string | - | Additional CSS classes |