Single-value dropdown picker built on Base UI Select. Renders a labelled input-style trigger with a floating option list.
Reach for Select when the user picks one value from a fixed list and no text filtering is needed.
Combobox.MultiSelect.Use @uxf/ui/select for a controlled select outside a form. Inside a @uxf/form form, use @uxf/form/select instead — it registers the field with react-hook-form and derives isInvalid / helperText from validation. This @uxf/ui component is the controlled primitive the form twin wraps.
import { Select, SelectOption } from "@uxf/ui/select";
import { useState } from "react";
const options: SelectOption[] = [
{ id: "one", label: "Option one" },
{ id: "two", label: "Option two", disabled: true },
{ id: "three", label: "Option three" },
];
function Example() {
const [value, setValue] = useState<string | number | null>(null);
return (
<Select
label="Choose an option"
name="example"
onChange={setValue}
options={options}
placeholder="Select..."
value={value}
/>
);
}
value is the selected option's id (string | number) or null, and onChange is called with the new id — not the option object.
Exports: Select and the types SelectProps, SelectOption, SelectValue.
SelectProps<Value = SelectValue, Option = SelectOption<Value>> extends FormControlProps<Value | null> and Clearable. SelectValue is number | string; SelectOption is { id: Value; label: ReactNode; disabled?: boolean }.
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Required. Field label. |
name | string | — | Required. Field name (also set as data-name). |
value | Value | null | — | Required. Selected option id, or null. |
onChange | (value: Value | null) => void | — | Required. Called with the newly selected id (or null when cleared). |
options | Option[] | — | Required. Selectable options. |
placeholder | string | — | Shown when no option is selected. |
helperText | ReactNode | — | Text under the control; styled as an error when isInvalid. |
isClearable | boolean | false | Show a clear button that resets the value to null. |
isDisabled | boolean | false | Disable the control. |
isReadOnly | boolean | false | Prevent changes while staying focusable. |
isInvalid | boolean | false | Invalid styling; sets aria-invalid and styles helperText as an error. |
isRequired | boolean | false | Mark the field required (adds the label indicator). |
hiddenLabel | boolean | false | Visually hide the label (kept for assistive tech). |
renderOption | (option: Option, isSelected: boolean) => ReactNode | option.label | Custom renderer for each dropdown option. |
renderSelectedOption | (option: Option) => ReactNode | option.label | Custom renderer for the selected value in the trigger. |
keyExtractor | (option: Option) => string | number | option.id | Custom React key for options. |
noOptionsMessage | string | localized "No items found" | Empty-list message. |
iconName | IconName | arrow icon | Icon for the dropdown arrow. |
size | InputGroupSize | "default" | "small", "default", or "large". |
variant | InputGroupVariant | "default" | Input group variant. |
id | string | auto (useId) | Root id; the error-message id derives from it. |
onBlur / onFocus | FocusEventHandler<HTMLInputElement> | — | Focus handlers. |
Layout, dropdown, and ref props: className, style, form, leftAddon, rightAddon, leftElement, rightElement, inputArrow, dropdownClassName, dropdownEndContent, dropdownMatchesInputWidth (default true), dropdownMaxHeight (default 240), dropdownPlacement (default "bottom"), dropdownStrategy, inputRef, inputGroupRef, inputWrapperRef.
small, default, large (via size).isInvalid; combine with helperText to show an error message.isDisabled blocks all interaction; isReadOnly keeps focus but prevents changes.isClearable, a remove button appears once a value is selected (unless disabled/read-only).dropdownPlacement="top" flips the list above the input; Floating UI auto-flips on collision.dropdownEndContent pins content (typically an "add a new item" button) below the options. It stays visible while the list scrolls and when nothing matches, fits inside dropdownMaxHeight together with the list, and is not part of the arrow-key navigation. Static content (ReactNode) leaves the dropdown open; the function form (args) => ReactNode gets args.close(), which closes the dropdown without changing the value.The component composes the input, label, dropdown, form-component, and icon primitives, so import all of their stylesheets once in your global CSS:
@import url("@uxf/ui/css/icon.css");
@import url("@uxf/ui/css/dropdown.css");
@import url("@uxf/ui/css/label.css");
@import url("@uxf/ui/css/form-component.css");
@import url("@uxf/ui/css/input-basic.css");
@import url("@uxf/ui/css/input.css");
@import url("@uxf/ui/css/select.css");
Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.