Skip to main content

Select

A dropdown for choosing from a predefined list. It uses Base UI Select by default and Base UI Combobox when searchable is enabled. Options can be flat or grouped in every mode.

import { Select } from '@jbpark/ui-kit';

<Select
placeholder="Choose a framework"
options={[
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
]}
onChange={value => console.log(value)}
/>;
Selected: none

For a long list of predefined options, add searchable to show a search field inside the popup while keeping the same closed trigger. The label is required in this mode to give the trigger and popup accessible names. Searchable option and group labels must be strings so they can be filtered.

<Select
searchable
label="Choose a framework"
placeholder="Select a framework"
searchPlaceholder="Search frameworks"
options={[
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
]}
onChange={value => console.log(value)}
/>

Use the default Select for short lists. Use Input.Search when users should enter free-form text rather than choose a predefined value.

For multiple selections, combine searchable with multiple. The input and selected chips remain visible while filtering, and each chip has a remove button. Unlike the single searchable mode, the input sits alongside the chips outside the popup so selected values remain visible. The selected value is an array of option IDs; clearing it returns [].

<Select
searchable
multiple
label="Choose frameworks"
options={[
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
]}
defaultValue={['react']}
onChange={values => console.log(values)}
/>

Props​

PropTypeDefault
options(Option | OptionGroup)[]-
placeholderstring-
onChangestring / string | null / string[] callback, depending on mode-
classNamestring-
searchablebooleanfalse
multipletrue (with searchable)false
labelstring (searchable modes)-
searchPlaceholderstring (searchable modes)Search...
emptyTextReact.ReactNode (searchable modes)No results found.
allowClearboolean (searchable modes)true
classNames{ input?, content?, list?, item?, empty?, chips?, chip? }-
chipDescriptionstring (multiple mode)Keyboard removal hint
inputDescription(selectedCount: number) => string (multiple mode)Keyboard chip navigation hint
interface Option {
label: React.ReactNode;
value: string;
disabled?: boolean;
}

interface OptionGroup {
label: React.ReactNode;
options: Option[];
}

An OptionGroup entry (an options array instead of value) renders as a labeled group. Select also accepts the rest of Base UI's Select root props (e.g. value, defaultValue, disabled, name, required) except onValueChange, which is renamed to onChange above. In searchable mode, onChange receives string | null, because clearing a selection emits null. It accepts the corresponding Base UI Combobox root props instead of Select root props. In multiple mode, onChange receives string[]; chips can be removed with their buttons or by focusing a chip and pressing Backspace or Delete.

Labelling​

id reaches the element that receives focus in every mode: the trigger, or the search input in multiple mode. aria-labelledby, aria-describedby and aria-invalid go to the same element. In the default mode, pair id with an external <label htmlFor> (or pass aria-label). In searchable modes, label sets aria-label, so name the control from a visible label with aria-labelledby, which takes precedence.