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 five 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>Avatar Variant
Items with avatars and secondary text for user selection.
<Selector.Item
value="user1"
variant="avatar"
avatar={<Avatar />}
secondaryText="@username"
>
User Name
</Selector.Item>Icon with noWrap
Use the noWrap prop to prevent text from wrapping (replaces the old country variant).
<Selector.Item value="nok" variant="icon" noWrap icon={<FlagNO />}>
Norwegian Krone (NOK)
</Selector.Item>Detail Variant
Items with an icon, primary text, and right-aligned secondary text.
<Selector.Item
value="us"
variant="detail"
icon={<FlagUS />}
secondaryText="United States"
>
+1
</Selector.Item>Icon with large iconSize
Use the iconSize="lg" prop for larger icons (replaces the old company variant).
<Selector.Item value="notion" variant="icon" iconSize="lg" icon={<NotionLogo />}>
Notion HQ
</Selector.Item>All Variants Overview
Text (Default)
Icon
Avatar
Icon (noWrap)
Detail
Icon (large)
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' | 'avatar' | 'detail' | 'detailAligned' | 'text' | Item display variant |
| icon | React.ReactNode | - | Icon element (for icon, detail, detailAligned variants) |
| avatar | React.ReactNode | - | Avatar element (for avatar variant) |
| secondaryText | string | - | Secondary text (for avatar, detail, detailAligned variants) |
| iconSize | 'sm' | 'md' | 'lg' | 'md' | Icon size override |
| noWrap | boolean | false | Prevent text from wrapping |
| 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 |
| layout | 'list' | 'grid' | 'list' | Layout mode — use 'grid' with detailAligned items for column alignment |
| 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 |