Select
Dropdown select component for choosing a single value from a list. Supports searchable filtering, grouped options, keyboard navigation, clearable selection, and multiple visual variants. Built for professional editor interfaces.
Live Preview
Import
Section titled “Import”import { Select } from 'entangle-ui';<Select options={[ { value: 'normal', label: 'Normal' }, { value: 'multiply', label: 'Multiply' }, { value: 'screen', label: 'Screen' }, ]} value={blendMode} onChange={setBlendMode}/>Controlled vs Uncontrolled
Section titled “Controlled vs Uncontrolled”The Select component supports both controlled and uncontrolled patterns.
Controlled
// Controlledconst [value, setValue] = useState<string | null>('normal');<Select options={options} value={value} onChange={setValue} />
// Uncontrolled<Select options={options} defaultValue="normal" />Grouped Options
Section titled “Grouped Options”Options can be organized into groups with non-selectable headers.
Grouped Options
<Select label="Blend Mode" options={[ { label: 'Normal', options: [ { value: 'normal', label: 'Normal' }, { value: 'dissolve', label: 'Dissolve' }, ], }, { label: 'Darken', options: [ { value: 'multiply', label: 'Multiply' }, { value: 'darken', label: 'Darken' }, { value: 'color-burn', label: 'Color Burn' }, ], }, ]} value={mode} onChange={setMode}/>Searchable
Section titled “Searchable”Enable the searchable prop to show a filter input inside the dropdown. A custom filter function can be provided with filterFn.
Searchable
<Select searchable searchPlaceholder="Filter blend modes..." emptyMessage="No matching modes" options={allBlendModes} value={mode} onChange={setMode}/>// Custom filter logic<Select searchable filterFn={(option, query) => option.value.startsWith(query.toLowerCase())} options={options} value={value} onChange={setValue}/>Clearable
Section titled “Clearable”When clearable is enabled, a clear button appears when a value is selected.
The onChange value type follows clearable: without it the callback only ever
receives a real value (T), so no null check is needed; with clearable it can
also receive null when the selection is cleared.
Clearable
<Select clearable placeholder="Choose a material..." options={materials} value={material} onChange={setMaterial}/>Options with Icons
Section titled “Options with Icons”Each option can include an icon rendered before its label.
Options with Icons
<Select options={[ { value: 'mesh', label: 'Mesh', icon: <MeshIcon /> }, { value: 'light', label: 'Light', icon: <LightIcon /> }, { value: 'camera', label: 'Camera', icon: <CameraIcon /> }, ]} value={objectType} onChange={setObjectType}/>Full Width
Section titled “Full Width”By default the select shrinks to the width of its content, which is convenient
inside toolbars but means it collapses when placed in a flex row. Set
fullWidth to stretch the trigger to fill its container — ideal for property
panels and forms.
Full Width
<Select fullWidth label="Framework" options={frameworks} value={v} onChange={setV}/>Content-Sized Dropdown
Section titled “Content-Sized Dropdown”The open dropdown sizes itself to its widest option (clamped to the viewport),
so long option labels are never clipped even when the trigger is narrow. Use
minDropdownWidth to set a floor and fullWidth to also widen the trigger.
Content-Sized Dropdown
// The trigger is 160px, but the dropdown grows to fit the longest label.<Select label="Shading Model" options={[ { value: 'std', label: 'Standard' }, { value: 'pbr', label: 'Physically Based Rendering (Metallic / Roughness)', }, ]} value={model} onChange={setModel}/>Variants
Section titled “Variants”The variant prop controls the trigger button style.
Variants
<Select variant="default" options={opts} value={v} onChange={setV} /><Select variant="ghost" options={opts} value={v} onChange={setV} /><Select variant="filled" options={opts} value={v} onChange={setV} />Sizes
<Select size="sm" options={opts} value={v} onChange={setV} /><Select size="md" options={opts} value={v} onChange={setV} /><Select size="lg" options={opts} value={v} onChange={setV} />| Size | Trigger Height | Use case |
| ---- | -------------- | ---------------- |
| sm | 20px | Compact toolbars |
| md | 24px | Standard panels |
| lg | 32px | Prominent forms |
Labels and Helper Text
Section titled “Labels and Helper Text”Labels and Helper Text
<Select label="Render Engine" helperText="Select the rendering backend" required options={engines} value={engine} onChange={setEngine}/>Error State
Section titled “Error State”Error State
<Select label="Material" error errorMessage="A material is required" options={materials} value={material} onChange={setMaterial}/>Form Integration
Section titled “Form Integration”Use the name prop for native form submission. A hidden <input> is rendered with the selected value.
<Select name="blendMode" options={blendModes} value={mode} onChange={setMode} />| Prop | Type | Default | Description |
|---|---|---|---|
value | T | null | — | Selected value (controlled). |
defaultValue | T | — | Default selected value (uncontrolled). |
options | Array<SelectOptionItem<T> | SelectOptionGroup<T>> | — | Options to display — flat list or grouped. |
placeholder | string | 'Select...' | Placeholder text when no value is selected. |
searchable | boolean | false | Whether to show a search input inside the dropdown. |
searchPlaceholder | string | 'Search...' | Placeholder for the search input. |
filterFn | (option: SelectOptionItem<T>, query: string) => boolean | — | Custom filter function for searchable mode. |
emptyMessage | string | 'No results found' | Message shown when search yields no results. |
size | 'sm' | 'md' | 'lg' | 'md' | Size of the trigger button. |
variant | 'default' | 'ghost' | 'filled' | 'default' | Visual variant of the trigger button. |
label | string | — | Label displayed above the select. |
helperText | string | — | Helper text displayed below the select. |
error | boolean | false | Error state. |
errorMessage | string | — | Error message shown when in error state. |
disabled | boolean | false | Whether the select is disabled. |
required | boolean | false | Whether the select is required. |
clearable | boolean | false | Whether a clear button appears when a value is selected. |
maxDropdownHeight | number | 240 | Maximum height of the dropdown in pixels. |
minDropdownWidth | number | — | Minimum width of the dropdown in pixels. The dropdown still grows past this to fit its content. |
fullWidth | boolean | false | Whether the select stretches to fill the width of its container. |
name | string | — | Name attribute for hidden input (form submission). |
onChange | (value: T) => void | — | Callback when the selected value changes. The value type is discriminated by `clearable`: a non-clearable select emits `T`; a `clearable` select emits `T | null` (null comes from the clear button). |
onOpenChange | (open: boolean) => void | — | Callback when the dropdown open state changes. |
className | string | — | Additional CSS class names. |
style | CSSProperties | — | Inline styles. |
testId | string | — | Test identifier for automated testing. |
ref | Ref<HTMLButtonElement> | — | Ref to the trigger button element. |
SelectOptionItem
Section titled “SelectOptionItem”| Prop | Type | Description |
| ---------- | ----------- | ---------------------------------------- |
| value | T | Unique value used for selection. |
| label | string | Display label (falls back to value). |
| icon | ReactNode | Optional icon rendered before the label. |
| disabled | boolean | Whether this option is disabled. |
SelectOptionGroup
Section titled “SelectOptionGroup”| Prop | Type | Description |
| --------- | ----------------------- | ------------------------------------ |
| label | string | Group header label (non-selectable). |
| options | SelectOptionItem<T>[] | Options within this group. |
Accessibility
Section titled “Accessibility”- Trigger renders as a
<button>withrole="combobox"andaria-haspopup="listbox" - Dropdown renders as a
role="listbox"withrole="option"children - Grouped options use
role="group"witharia-label - Full keyboard navigation: Arrow Up/Down to navigate, Enter to select, Escape to close, Home/End for first/last
aria-expandedreflects the dropdown open statearia-selectedmarks the current selectionaria-invalidis set when in error statearia-requiredis set when the select is requiredaria-describedbylinks to helper/error text- Clear button has
aria-label="Clear selection"