Select
import { Select, useSelect } from '@tangible/ui';
Interactive docs and examples: Select in Storybook.
Select props
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
align? |
"center" | "end" | "start" |
'start' |
Alignment along the side. |
aria-describedby? |
string |
ID of element(s) that describe this select. | |
aria-invalid? |
"false" | "true" | boolean |
Marks the control invalid. Lands on the trigger, which also wears tui-input-group — that is what lights the danger border. Injected automatically by Field.Control when the Field has an error. |
|
aria-label? |
string |
Accessible name for the select. | |
aria-labelledby? |
string |
ID of element that labels this select. | |
aria-required? |
"false" | "true" | boolean |
Marks the control required. Injected automatically by Field.Control when the Field is required. |
|
clearable? |
boolean |
false |
Whether the selection can be cleared with a clear button. |
clearLabel? |
string |
'Clear selection' |
Accessible label for the clear button (i18n). |
defaultOpen? |
boolean |
false |
Default open state for uncontrolled usage. |
defaultValue? |
OptionValue |
Default value for uncontrolled usage. | |
disabled? |
boolean |
false |
Whether the entire select is disabled. |
id? |
string |
Optional trigger ID override. Useful for Field integration via htmlFor. | |
onOpenChange? |
(open: boolean) => void |
Callback when open state changes. | |
onValueChange? |
(value: OptionValue | undefined) => void |
Callback when selection changes. Called with undefined when selection is cleared. | |
open? |
boolean |
Controlled open state. | |
placeholder? |
string |
'' |
Placeholder text when no value is selected. |
side? |
"bottom" | "top" |
'bottom' |
Preferred side of the trigger to place the listbox. |
sideOffset? |
number |
4 |
Distance from the trigger in pixels. |
size? |
"lg" | "md" | "sm" |
'md' |
Control size. |
value? |
OptionValue |
Controlled selected value. |
Select.Trigger
Also exported as SelectTrigger.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild? |
boolean |
false |
When true, merges props onto the child element instead of rendering the default trigger button. Child must be a single React element that accepts ref, ARIA attributes, and event handlers (onClick, onKeyDown, onBlur, onMouseDown, onPointerDown). Note: aria-label and aria-labelledby from Select Root are spread onto the child but may be overridden by the child's own props. Ensure the child does not silently replace these if the Root provides them. |
children? |
React.ReactNode |
||
className? |
string |
Additional CSS class names. |
Select.Content
Also exported as SelectContent.
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
className? |
string |
Additional CSS class names. |
Select.Option
Also exported as SelectOption.
| Prop | Type | Default | Description |
|---|---|---|---|
children |
React.ReactNode |
||
value |
OptionValue |
The value for this option. Required and must be unique. Can be string or number. | |
className? |
string |
Additional CSS class names. | |
disabled? |
boolean |
false |
Whether this option is disabled. |
textValue? |
string |
Text value for typeahead. Required when children is not a string. If children is a string, defaults to that. |
Select.Group
Also exported as SelectGroup.
| Prop | Type | Default | Description |
|---|---|---|---|
children? |
ReactNode |
||
className? |
string |
Select.Label
Also exported as SelectLabel.
| Prop | Type | Default | Description |
|---|---|---|---|
children? |
ReactNode |
||
className? |
string |
Hooks
useSelect
useSelect(): SelectContextValue
Access Select context: config, IDs, refs, callbacks, and reactive state.
Returns:
| Member | Type | Description |
|---|---|---|
disabled |
boolean |
|
placeholder |
string |
|
clearable |
boolean |
|
clearLabel |
string |
|
size |
"lg" | "md" | "sm" |
|
triggerId |
string |
|
listboxId |
string |
|
ariaLabel? |
string |
|
ariaLabelledBy? |
string |
|
ariaDescribedBy? |
string |
|
ariaInvalid? |
"false" | "true" | boolean |
|
ariaRequired? |
"false" | "true" | boolean |
|
setOpen |
(open: boolean) => void |
|
setValue |
(value: OptionValue | undefined, textValue?: string) => void |
|
registerOption |
(option: RegisteredOption) => void |
|
unregisterOption |
(value: OptionValue) => void |
|
handleSelect |
(index: number | null) => void |
|
refs |
{ reference: React.RefObject<ReferenceType | null>; floating: React.RefObject<HTMLElement | null>; setReference: (node: ReferenceType | null) => void; setFloating: (node: HTMLElement | null) => void; } |
|
listRef |
MutableRefObject<(HTMLElement | null)[]> |
|
justClearedRef |
MutableRefObject<boolean> |
Guard flag — prevents Floating UI from opening dropdown during a clear |
getReferenceProps |
(userProps?: React.HTMLProps<Element>) => Record<string, unknown> |
|
getFloatingProps |
(userProps?: React.HTMLProps<HTMLElement>) => Record<string, unknown> |
|
getItemProps |
(userProps?: React.HTMLProps<HTMLElement>) => Record<string, unknown> |
|
open |
boolean |
|
value |
OptionValue | undefined |
|
displayText |
string | undefined |
|
activeIndex |
number | null |
|
highlightedValue |
OptionValue | null |
|
orderedOptions |
RegisteredOption[] |
|
optionIndexMap |
Map<string, number> |
O(1) index lookup: toKey(value) → index in orderedOptions |
floatingStyles |
CSSProperties |
RegisteredOption (the option of registerOption):
| Member | Type | Description |
|---|---|---|
value |
OptionValue |
|
ref |
RefObject<HTMLElement | null> |
|
disabled |
boolean |
|
textValue |
string |
Examples
-
Controlled: Demonstrates controlled value with external buttons to set/clear selection.
-
Controlled Open State: Demonstrates controlled open/close state with external toggle buttons.
-
Typeahead: Type characters while open to jump to matching options (e.g. "bl" → Blueberry).
-
Keyboard Navigation: Shows keyboard controls: arrow keys, Home/End, Enter/Space, Escape, Tab.
-
In Modal: Verifies Escape closes Select before Modal, and portal renders inside .tui-interface.
-
Placement: Dropdown placement: bottom (default) vs top.
-
Alignment: Content alignment relative to trigger: start, center, end.
-
Form Example: Multi-field form using Field + Field.Label + Field.Control with grouped options.
-
Static (Inspect): Static markup for devtools inspection — not interactive. Shows trigger and content states.
-
Sizes: Side-by-side comparison of sm, md, and lg size variants.
-
Clearable: Demonstrates the clear button and Delete keyboard shortcut for clearing selection.
-
With Field: Select inside Field with Field.Label and Field.Control for proper label association.
-
With Field Error: Select inside Field with Field.Error — demonstrates aria-invalid state and error message.
-
With Field Required: Required variant —
Field.Controlinjectsaria-requiredonto the trigger. -
asChild Trigger: Custom trigger via asChild — merges Select props onto the child element instead of rendering a default button.
Other stories: Basic, Placeholder, Default Value, Disabled, Disabled Options, Groups, Long List, With Icons (interactive page).
Component API tokens
Read via fallback, never declared. Set one on an ancestor to restyle every instance under it.
--tui-select-min-width--tui-select-trigger-bg--tui-select-trigger-fg--tui-select-trigger-border--tui-select-trigger-border-hover--tui-select-trigger-border-focus--tui-select-trigger-radius--tui-select-content-bg--tui-select-content-fg--tui-select-content-border--tui-select-content-shadow--tui-select-content-radius--tui-select-option-radius--tui-select-option-bg-hover--tui-select-option-bg-selected--tui-select-option-fg-hover--tui-select-option-fg-selected--tui-select-option-bg-selected-hover--tui-select-option-check--tui-select-option-gap