Skip to main content

Select

Source ↗

A Select is a form control that lets users pick one or more options from a dropdown list. It is built on top of the Popover component and supports sections, search filtering, multi-select, and keyboard navigation out of the box.

Key Characteristics

  • Flexible Data: Provide options via the items prop or compose them declaratively with Select.Section and Select.Item.
  • Multi-Select: Toggle multiple to allow selecting more than one option, with chip-based value display.
  • Searchable: Enable the search prop to let users filter options by typing.
  • Accessible: Full keyboard navigation and focus management built in.

Import

There are 4 select-related components:

  • Select: The main select component.
  • SelectItem: An individual option.
  • SelectSection: A group of options with an optional title.
  • SelectDivider: A visual separator between sections.
import { Select, SelectItem, SelectSection, SelectDivider } from '@andrejground/lab';

Compound pattern is also supported:

  • Select.Item
  • Select.Section
  • Select.Divider

Customization

Add your styles and your component is ready to be used.

import { OptionItem, Select, SelectProps } from '@andrejground/lab';
import styles from './MySelect.module.scss';

type Props<T extends OptionItem> = SelectProps<T>;

function MySelect<T extends OptionItem>(props: Props<T>) {
return (
<Select
{...props}
classNames={{
popover: { content: styles.popoverContent },
trigger: {
base: styles.triggerBase,
valueChip: styles.triggerValueChip,
},
section: { title: styles.sectionTitle },
item: { base: styles.itemBase },
}}
/>
);
}

MySelect.Section = Select.Section;
MySelect.Item = Select.Item;
MySelect.Divider = Select.Divider;

export default MySelect;

Usage

Favorite animal

Sections

Use Select.Section and Select.Item to group options with titles and descriptions.

Grouped items

Multiple

Enable multiple to allow selecting more than one option. Selected values appear as removable chips.

Multiple selection

Searchable

Enable search to let users filter options by typing in the trigger input.

Searchable select

Add New Option

Enable isAddNewOption together with search to let users add custom options that don't exist in the list. When the search query doesn't match any existing option, an "Add <value>" action appears.

Add new option

Combine multiple, search, and popOnSelection for an autocomplete-style multi-select. Selected options are removed from the dropdown list.

Multi-select with search

Truncation Off

With all truncate options set to false, long option text, descriptions, and selected value chips wrap naturally into multiple lines.

Truncation off (text wraps)

Truncation On

With all truncate options set to true, long text is clipped to a single line with an ellipsis - for item text, item descriptions, value chips, and the selected value display.

Truncation on (text truncated)

Async & Infinite Scroll

Use infiniteScrollProps to load more options as the user scrolls. Combine with search, onSearchChange, and debounceCallback for a full async autocomplete experience.

Loading...

Controlled

Controlled select
Open: false

API

Select Props

PropTypeDefaultDescription
childrenReactNode | ((item: T) => ReactNode)-Select content using composition pattern or render function
triggerReactNode-Element that triggers the select
itemsOptionItem[]-The list of selectable options
valueT[]-The currently selected options (controlled mode)
defaultValueT[]-The initially selected options (uncontrolled mode)
placeholderstring"Select"Placeholder text shown when no value is selected
multiplebooleanfalseEnables multi-select mode
searchboolean | ((items: T[]) => T[])-Enables search filtering; pass a function for custom filtering
labelReactNode-Label displayed above or beside the select
isRequiredboolean-Marks the select as required and shows an asterisk
fullWidthbooleanfalseMakes the select take the full width of its container
isOpenboolean-Controls the select open state (controlled mode)
isDisabledboolean-Disables the select trigger
isLoadingboolean-Shows a loading indicator inside the select
autoFocus"first-item" | "last-item" | "menu" | "none""menu"Controls which item receives focus when the select opens
sizePopoverSize"trigger"Controls the width of the select popover
backdropBackdrop-The backdrop style displayed behind the select
shouldFlipbooleantrueFlips placement when there is not enough space
shouldBlockScrollbooleanfalseBlocks page scroll when the select is open
shouldCloseOnScrollbooleanfalseCloses the select when the page is scrolled
shouldCloseOnClickOutsidebooleantrueCloses the select when clicking outside of it
shouldCloseOnEscbooleantrueCloses the select when the Escape key is pressed
shouldCloseOnSelectionboolean-Whether selecting an item closes the select
showArrowbooleanfalseShows an arrow pointing toward the trigger
offsetnumber-Distance in pixels between the select and the trigger
growContentboolean-Allows the select content to grow beyond the trigger width
openOnLabelClickboolean-Opens the select when the label is clicked
popOnSelectionboolean-Removes selected items from the options list
isAddNewOptionboolean-Shows "Add <value>" option when no results are found
addNewLabelstring"Add"Custom label prefix for the "Add new" option
clearSearchOnSelectionbooleantrueClears the search input when an option is selected
topContentReactNode-Content rendered above the listbox
bottomContentReactNode-Content rendered below the listbox
caretReactNode-Custom caret element replacing the default indicator
showCaretboolean-Shows or hides the caret indicator
truncateSelectTruncate{ itemText: false, valueChipText: true, itemDescription: false, sectionTitle: true, valueText: true }Controls text truncation for various select parts
descriptionReactNode-Helper text displayed below the select
errorMessageReactNode-Error message displayed below the select
noResultsMessageReactNode-Message displayed when search yields no results
renderOptionRenderOption-Custom render function for each option in the listbox
renderValue(selectedItems: T[]) => ReactNode-Custom render function for the selected value display
infiniteScrollPropsInfiniteScrollProps-Configuration for infinite scrolling within the listbox
onSelectionChange(value: T[], action: "added" | "removed", item: T) => void-Callback fired when the selection changes
onClose(selectedItems?: T[]) => void-Callback fired when the select popover closes
onSearchChange(searchQuery: string) => void-Callback fired when the search query changes
onAddNewOption(newOption: T) => void-Callback fired when a new option is added
onOpen() => void-Callback fired when the select opens
onClickOutside() => void-Callback fired when clicking outside the select
onOpenChange(isOpen: boolean) => void-Callback fired when the open state changes
onTriggerFocus() => void-Callback fired when the trigger receives focus
onTriggerBlur() => void-Callback fired when the trigger loses focus
triggerWrapperboolean-Wraps the trigger in a span instead of using Slot
fullWidthTriggerWrapperboolean-Makes the trigger wrapper take full width
focusTrapProps{ trapFocus?: boolean; autoFocus?: boolean }{ trapFocus: true, autoFocus: autoFocus === "none" }Configuration for focus trap behavior
classNamesSelectClassNames-Custom class names for the select slots

Select.Item Props

Extends OptionItem (value, text, textContent, description, disabled).

PropTypeDefaultDescription
childrenReactNode-Item content
valuestring | numberrequiredUnique identifier for the option
textstringrequiredDisplay text for the option
shouldCloseOnSelectionbooleantrueWhether selecting this item closes the select
disabledboolean-Disables the item
showDisabledStylesbooleantrueShows disabled visual styles without disabling interaction
startContentReactNode-Content rendered before the item text
endContentReactNode-Content rendered after the item text
descriptionReactNode-Additional description displayed below the option text
onAddNewOption(newOption: T) => void-Callback fired when a new option is added via this item
truncateSelectItemTruncate-Override global truncate settings for this item
classNamesSelectItemClassNames-Custom class names for the item slots

Select.Section Props

PropTypeDefaultDescription
childrenReactNode-Section content
titleReactNode-Title displayed above the section
isStickyTitlebooleantrueKeeps the section title visible while the section scrolls
showDividerboolean-Shows a visual divider below this section
truncateSelectSectionTruncate-Override global truncate settings for this section
classNamesSelectSectionClassNames-Custom class names for the section slots

Select.Divider Props

PropTypeDefaultDescription
classNamesSelectDividerClassNames-Custom class names for the divider slots