ComponentsUpdated Sep 9, 2026
Select
A single-choice picker. Pass items to the root so the trigger can show the selected label and size itself to the widest option.
Import
Source: packages/ui/src/components/select
tsx
import { Select, SelectItemType } from '@highlight/ui'Model
tsx
import { Select, type SelectItemType } from '@highlight/ui'
const MODELS: Array<SelectItemType> = [
{ label: 'Claude Fable 5.1', value: 'fable' },
{ label: 'Claude Opus 5', value: 'opus' },
{ label: 'Claude Sonnet 5', value: 'sonnet' },
{ label: 'Claude Haiku 4.5', value: 'haiku' },
]
export default function SelectBasic() {
return (
<Select items={MODELS} defaultValue="fable">
<Select.Label>Model</Select.Label>
<Select.Trigger />
<Select.Content>
{MODELS.map((item) => (
<Select.Item key={String(item.value)} value={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select>
)
}Trigger sizes
default, compact, and badge. With a placeholder, badge collapses to plain text until a value is chosen.
tsx
import { Select, type SelectItemType } from '@highlight/ui'
const STATUSES: Array<SelectItemType> = [
{ label: 'Todo', value: 'todo' },
{ label: 'In progress', value: 'in-progress' },
{ label: 'Done', value: 'done' },
]
function StatusItems() {
return (
<Select.Content>
{STATUSES.map((item) => (
<Select.Item key={String(item.value)} value={item.value}>
{item.label}
</Select.Item>
))}
</Select.Content>
)
}
export default function SelectSizes() {
return (
<div className="flex flex-wrap items-end justify-center gap-4">
<Select items={STATUSES} defaultValue="todo">
<Select.Trigger />
<StatusItems />
</Select>
<Select items={STATUSES} defaultValue="in-progress">
<Select.Trigger size="compact" />
<StatusItems />
</Select>
<Select items={STATUSES} defaultValue="done">
<Select.Trigger size="badge" />
<StatusItems />
</Select>
<Select items={STATUSES}>
<Select.Trigger size="badge" placeholder="+ Status" />
<StatusItems />
</Select>
</div>
)
}Visibility
Selected: team
tsx
import { useState } from 'react'
import { Globe, Lock, Users } from 'lucide-react'
import { Select, type SelectItemType } from '@highlight/ui'
const VISIBILITY: Array<SelectItemType> = [
{ label: 'Private', value: 'private', icon: <Lock /> },
{ label: 'Team', value: 'team', icon: <Users /> },
{ label: 'Anyone with the link', value: 'public', icon: <Globe /> },
]
export default function SelectIcons() {
const [value, setValue] = useState<string | null>('team')
return (
<div className="flex flex-col items-start gap-2">
<Select items={VISIBILITY} value={value} onValueChange={(next) => setValue(next as string | null)}>
<Select.Label>Visibility</Select.Label>
<Select.Trigger placeholder="Choose…" />
<Select.Content>
{VISIBILITY.map((item) => (
<Select.Item key={String(item.value)} value={item.value} icon={item.icon}>
{item.label}
</Select.Item>
))}
</Select.Content>
</Select>
<p className="text-foreground/60 text-xs">Selected: {value ?? 'none'}</p>
</div>
)
}Parts
Compound sub-components. Every part accepts className and forwards the rest of its props to Base UI.
Select.Label- Text above the trigger.
Select.Trigger- size: 'default' | 'compact' | 'badge'; placeholder; hideIcon; minWidth.
Select.Content- Portal + Positioner + Popup with scroll arrows. showScrollArrows, positionerProps.
Select.Item- value, icon, onClick.
Select.Value / Icon- Base UI parts for custom triggers.
Props
Props added on top of the underlying element or Base UI part.
| Prop | Type | Default | Description |
|---|---|---|---|
items | Array<SelectItemType> | — | label, value, optional icon. Drives the trigger label and width. |
autoWidth | boolean | true | Size the trigger to the widest label. |
minWidth | string | — | Explicit trigger min-width; disables autoWidth. |