moduix

Select

A form control for choosing one or more predefined values from a popup list.

API Reference

Original primitive API

Behavior, accessibility details, and low-level props are documented by Base UI.

Base UI API

Choosing the right component

Use Select for strict, predefined choices where the trigger behaves like a button and users do not type into the field itself.

  • Choose Select for canonical form pickers with fixed options.
  • Choose Combobox when users should search/filter predefined options via text input.
  • Choose Autocomplete when users may enter arbitrary values beyond suggested options.

Basic

Choose fruit
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectScrollUpArrow,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,  SelectScrollDownArrow,} from "moduix";export function SelectDemo() {  return (    <Select items={fruits}>      <SelectField>        <SelectLabel>Choose fruit</SelectLabel>        <SelectTrigger className={styles.customTrigger}>          <SelectValue placeholder="Select an option" />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectScrollUpArrow />        <SelectList>          {fruits.map((item) => (            <SelectItem key={item.value} value={item.value}>              <SelectItemIndicator />              <SelectItemText>{item.label}</SelectItemText>            </SelectItem>          ))}        </SelectList>        <SelectScrollDownArrow />      </SelectContent>    </Select>  );}
const fruits = [  { label: "Apple", value: "apple" },  { label: "Banana", value: "banana" },  { label: "Blueberry", value: "blueberry" },  { label: "Grape", value: "grape" },  { label: "Kiwi", value: "kiwi" },  { label: "Mango", value: "mango" },  { label: "Orange", value: "orange" },  { label: "Pineapple", value: "pineapple" },  { label: "Strawberry", value: "strawberry" },  { label: "Watermelon", value: "watermelon" },];

Full list of component variables available for project-level overrides.

PropertyDefaultDescription
--select-arrow-height0.625remControls popup arrow height.
--select-arrow-inline-offset0.8125remControls popup arrow inline offset.
--select-arrow-size0.5remControls popup arrow side offset size.
--select-arrow-stroke-colorvar(--select-popup-border-color)Controls popup arrow stroke color.
--select-arrow-width1.25remControls popup arrow width.
--select-backdrop-bgvar(--backdrop-bg)Controls backdrop color.
--select-backdrop-blur2pxControls backdrop blur.
--select-backdrop-transitionvar(--transition-default)Controls backdrop transition timing.
--select-bgvar(--color-background)Controls trigger background.
--select-bg-activevar(--color-muted)Controls trigger background when popup is open.
--select-bg-hovervar(--color-accent)Controls trigger hover background.
--select-border-colorvar(--color-border)Controls trigger border color.
--select-border-widthvar(--border-width-sm)Controls trigger border width.
--select-colorvar(--color-foreground)Controls main text color.
--select-control-heightvar(--size-lg)Controls trigger minimum height.
--select-disabled-opacityvar(--opacity-disabled)Controls disabled opacity.
--select-field-gap0.375remControls field gap between label and trigger.
--select-focus-ring-colorvar(--color-ring)Controls focus ring color.
--select-focus-ring-offset-1pxControls focus ring offset.
--select-focus-ring-widthvar(--select-border-width)Controls focus ring width.
--select-group-label-bgvar(--select-popup-bg)Controls group label background.
--select-group-label-colorvar(--color-muted-foreground)Controls group label color.
--select-group-label-font-sizevar(--text-xs)Controls group label font size.
--select-group-label-font-weightvar(--weight-semibold)Controls group label font weight.
--select-group-label-line-heightvar(--line-height-text-xs)Controls group label line height.
--select-group-label-padding-bottom0.35remControls group label bottom padding.
--select-group-label-padding-top0.35remControls group label top padding.
--select-group-label-padding-x0.625remControls group label horizontal padding.
--select-group-padding-bottomvar(--spacing-1)Controls group bottom padding.
--select-highlight-bgvar(--color-foreground)Controls highlighted item background.
--select-highlight-colorvar(--color-background)Controls highlighted item text color.
--select-highlight-inset-xvar(--spacing-1)Controls highlighted item horizontal inset.
--select-highlight-radiusvar(--radius-sm)Controls highlighted item radius.
--select-icon-colorvar(--color-muted-foreground)Controls trigger icon color.
--select-icon-size0.875remControls trigger icon wrapper size.
--select-icon-svg-size1remControls trigger icon SVG size.
--select-item-colorvar(--color-foreground)Controls item text color.
--select-item-font-sizevar(--text-sm)Controls item font size.
--select-item-gap0.5remControls item grid gap.
--select-item-indicator-icon-size0.75remControls item indicator icon size.
--select-item-indicator-size0.75remControls item indicator slot size.
--select-item-line-heightvar(--line-height-text-sm)Controls item line height.
--select-item-min-height2remControls item minimum height.
--select-item-padding-x-end1remControls item end padding.
--select-item-padding-x-start0.625remControls item start padding.
--select-item-padding-y0.5remControls item vertical padding.
--select-item-radius0Controls item radius.
--select-item-text-content-gapvar(--spacing-2)Controls item text content gap.
--select-item-text-icon-colorcurrentColorControls item text icon color.
--select-item-text-icon-size1remControls item text icon size.
--select-label-font-sizevar(--text-sm)Controls label font size.
--select-label-font-weightvar(--weight-medium)Controls label font weight.
--select-label-line-heightvar(--line-height-text-sm)Controls label line height.
--select-list-max-heightvar(--select-popup-max-height)Controls list max height.
--select-list-padding-y0.25remControls list vertical padding.
--select-list-scroll-padding-y0.25remControls list scroll padding.
--select-overlap-offset1remControls overlap offset for static side positioning.
--select-placeholder-colorvar(--color-muted-foreground)Controls placeholder color.
--select-popup-bgvar(--color-popover)Controls popup background.
--select-popup-border-colorvar(--color-border)Controls popup border color.
--select-popup-border-widthvar(--border-width-sm)Controls popup border width.
--select-popup-max-height24remControls popup max height.
--select-radiusvar(--radius-md)Controls trigger and popup radius.
--select-scroll-arrow-colorvar(--select-color)Controls scroll arrow color.
--select-scroll-arrow-height1remControls scroll arrow height.
--select-scroll-arrow-icon-size0.875remControls scroll arrow icon size.
--select-scroll-arrow-z-index1Controls scroll arrow stacking order.
--select-scalevar(--scale-popup)Controls popup scale animation value.
--select-separator-colorvar(--select-border-color)Controls separator color.
--select-separator-margin-x1remControls separator horizontal margin.
--select-separator-margin-y0.375remControls separator vertical margin.
--select-separator-thicknessvar(--border-width-sm)Controls separator thickness.
--select-shadowvar(--shadow-lg)Controls popup shadow.
--select-transitionvar(--transition-default)Controls popup transition timing.
--select-trigger-gap0.75remControls trigger content gap.
--select-trigger-padding-x0.875remControls trigger horizontal padding.
--select-width14remControls trigger and popup anchor width.

Interactive variables scoped for docs preview without changing size scale tokens.

PropertyValueDefaultDescription
--select-bgvar(--color-background)Controls trigger background.
--select-bg-activevar(--color-muted)Controls trigger background when open.
--select-bg-hovervar(--color-accent)Controls trigger background on hover.
--select-border-colorvar(--color-border)Controls trigger border color.
--select-colorvar(--color-foreground)Controls primary text color.
--select-focus-ring-colorvar(--color-ring)Controls keyboard focus ring color.
--select-highlight-bgvar(--color-foreground)Controls highlighted item background.
--select-highlight-colorvar(--color-background)Controls highlighted item text color.
--select-popup-bgvar(--color-popover)Controls popup background.
--select-popup-border-colorvar(--color-border)Controls popup border color.
--select-radiusvar(--radius-md)Controls trigger and popup radius.

Anatomy

Select combines a trigger-like field with a popup list of options. Keep field parts grouped inside SelectField and list parts inside SelectContent so labeling, selection state, and keyboard behavior stay synchronized.

Select
├─ SelectField
│  ├─ SelectLabel
│  └─ SelectTrigger
│     ├─ SelectValue
│     └─ SelectIcon
└─ SelectContent
   ├─ service slots (internal): portal, backdrop, positioner, arrow
   ├─ SelectScrollUpArrow / SelectScrollDownArrow (optional)
   └─ SelectList
      └─ SelectItem[value]
         ├─ SelectItemIndicator
         └─ SelectItemText
<Select items={fruits}>
  <SelectField>
    <SelectLabel>Choose fruit</SelectLabel>
    <SelectTrigger>
      <SelectValue placeholder="Select an option" />
      <SelectIcon />
    </SelectTrigger>
  </SelectField>

  <SelectContent>
    <SelectScrollUpArrow />
    <SelectList>
      {fruits.map((item) => (
        <SelectItem key={item.value} value={item.value}>
          <SelectItemIndicator />
          <SelectItemText>{item.label}</SelectItemText>
        </SelectItem>
      ))}
    </SelectList>
    <SelectScrollDownArrow />
  </SelectContent>
</Select>
PartRole
SelectRoot state machine. Controls selected value(s), popup open state, keyboard navigation, and item string mapping.
SelectFieldField wrapper for label and trigger. Keeps form semantics and visible control layout together.
SelectTriggerInteractive button that opens/closes the popup and exposes open/disabled state attributes.
SelectValueValue renderer inside the trigger. Shows placeholder, selected label, or a custom function output.
SelectContentPopup surface that hosts list/scroll controls and configures internal service layers and positioning props.
SelectItemOne selectable option. Works with SelectItemIndicator and SelectItemText to render selection state.

In most cases, keep default popup infrastructure and style visible parts (SelectTrigger, SelectValue, SelectContent, SelectItem, SelectItemIndicator, SelectItemText). Use SelectContent service-slot props (classNames, slotProps, withBackdrop, arrow) only when you need custom portal, layering, backdrop, or arrow behavior.

Composition

Use Select for root state and behavior props such as value, defaultValue, multiple, onValueChange, items, itemToStringLabel, and itemToStringValue. Base UI root props pass through as well: form props (name, form, required, inputRef, autoComplete), interaction state (disabled, readOnly, open, defaultOpen, modal, onOpenChange, onOpenChangeComplete, actionsRef), and item behavior (highlightItemOnHover, isItemEqualToValue).

className styles the visible popup. classNames styles the internal service slots that are hidden from the default composition. Use positioning props such as alignItemWithTrigger, side, sideOffset, align, collisionPadding, and disableAnchorTracking directly on SelectContent; use container for the portal target and slotProps for the matching Base UI escape hatches:

<SelectContent
  className={styles.popup}
  withArrow
  withBackdrop
  slotProps={{
    positioner: { sticky: true },
  }}
  classNames={{
    portal: styles.portal,
    backdrop: styles.backdrop,
    positioner: styles.positioner,
    arrow: styles.arrow,
  }}
/>

Examples

Indicator Right With Icon

Use indicator="end" to place the selected indicator after the item text. Pass children to SelectIcon or SelectItemTextIcon to use icons from your application.

Choose fruit
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  ChevronDownIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemText,  SelectItemTextContent,  SelectItemTextIcon,  InfoIcon,  SelectItemTextLabel,  SelectItemIndicator,} from "moduix";export function IndicatorRightSelectDemo() {  return (    <Select items={fruits}>      <SelectField>        <SelectLabel>Choose fruit</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select an option" />          <SelectIcon>            <ChevronDownIcon className={styles.customTriggerIcon} />          </SelectIcon>        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectList>          {fruits.map((item) => (            <SelectItem key={item.value} value={item.value} indicator="end">              <SelectItemText>                <SelectItemTextContent>                  <SelectItemTextIcon>                    <InfoIcon className={styles.statusIcon} />                  </SelectItemTextIcon>                  <SelectItemTextLabel>{item.label}</SelectItemTextLabel>                </SelectItemTextContent>              </SelectItemText>              <SelectItemIndicator />            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
.customTriggerIcon {  color: var(--color-foreground);}.statusIcon {  color: var(--color-muted-foreground);}
const fruits = [  { label: "Apple", value: "apple" },  { label: "Banana", value: "banana" },  { label: "Mango", value: "mango" },];

Grouped

Use SelectGroup and SelectGroupLabel to organize related items inside the popup.

Choose produce
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectGroup,  SelectSeparator,  SelectGroupLabel,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";export function GroupedSelectDemo() {  return (    <Select>      <SelectField>        <SelectLabel>Choose produce</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select item">            {(value) =>              typeof value === "string" ? groupedLabelByValue[value] ?? value : "Select item"            }          </SelectValue>          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectList>          {groupedOptions.map((group, index) => (            <SelectGroup key={group.label}>              {index > 0 ? <SelectSeparator /> : null}              <SelectGroupLabel>{group.label}</SelectGroupLabel>              {group.items.map((item) => (                <SelectItem key={item.value} value={item.value}>                  <SelectItemIndicator />                  <SelectItemText>{item.label}</SelectItemText>                </SelectItem>              ))}            </SelectGroup>          ))}        </SelectList>      </SelectContent>    </Select>  );}
const groupedOptions = [  {    label: "Fruits",    items: [      { label: "Apple", value: "apple" },      { label: "Mango", value: "mango" },    ],  },  {    label: "Vegetables",    items: [      { label: "Carrot", value: "carrot" },      { label: "Spinach", value: "spinach" },    ],  },];const groupedLabelByValue = Object.fromEntries(  groupedOptions.flatMap((group) => group.items.map((item) => [item.value, item.label])),);

Animated

Pass animation="scale" to SelectContent when the popup should animate in the default overlapping select positioning mode.

Choose fruit
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";export function AnimatedSelectDemo() {  return (    <Select items={fruits}>      <SelectField>        <SelectLabel>Choose fruit</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select an option" />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent animation="scale" className={styles.animatedContent}>        <SelectList>          {fruits.map((item) => (            <SelectItem key={item.value} value={item.value}>              <SelectItemIndicator />              <SelectItemText>{item.label}</SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
.animatedContent {  --select-transition: 180ms var(--ease-out);  --select-scale: 0.96;}
const fruits = [  { label: "Apple", value: "apple" },  { label: "Banana", value: "banana" },  { label: "Mango", value: "mango" },];

Multiple

Use the multiple prop when a field can contain several selected values. Render SelectValue with a function to format the array.

Languages
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";function renderMultipleValue(value) {  if (value.length === 0) {    return "Select languages";  }  const first = languages[value[0]];  const suffix = value.length > 1 ? ` (+${value.length - 1})` : "";  return `${first}${suffix}`;}export function MultipleSelectDemo() {  return (    <Select multiple defaultValue={["javascript", "typescript"]}>      <SelectField>        <SelectLabel>Languages</SelectLabel>        <SelectTrigger>          <SelectValue>{renderMultipleValue}</SelectValue>          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent alignItemWithTrigger={false}>        <SelectList>          {languageValues.map((value) => (            <SelectItem key={value} value={value}>              <SelectItemIndicator />              <SelectItemText>{languages[value]}</SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
const languages = {  javascript: "JavaScript",  python: "Python",  rust: "Rust",  typescript: "TypeScript",};const languageValues = Object.keys(languages);

Custom Styles

Use SelectContent props for popup infrastructure. Portal, Positioner, Backdrop, and Arrow are rendered internally, so application code only configures behavior and service-slot classes.

Choose fruit
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";export function CustomStylesSelectDemo() {  return (    <Select items={fruits}>      <SelectField>        <SelectLabel>Choose fruit</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select an option" />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent        alignItemWithTrigger={false}        sideOffset={8}        withArrow        withBackdrop        slotProps={{          positioner: { sticky: true },        }}        className={styles.customPopup}        classNames={{          portal: styles.customPortal,          backdrop: styles.customBackdrop,          positioner: styles.customPositioner,          arrow: styles.customArrow,        }}      >        <SelectList>          {fruits.map((item) => (            <SelectItem key={item.value} value={item.value}>              <SelectItemIndicator />              <SelectItemText>{item.label}</SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
.customPortal {  pointer-events: auto;}.customBackdrop {  --select-backdrop-bg: rgb(15 23 42 / 0.48);  --select-backdrop-blur: 3px;}.customPositioner {  filter: drop-shadow(var(--shadow-md));}.customTrigger {  position: relative;  z-index: calc(var(--z-popup) + 1);}.customArrow {  --select-arrow-stroke-color: var(--select-popup-border-color);}.customPopup {  --select-radius: var(--radius-lg);  --select-popup-bg: var(--color-background);  --select-shadow: var(--shadow-xl);}
const fruits = [  { label: "Apple", value: "apple" },  { label: "Banana", value: "banana" },  { label: "Mango", value: "mango" },];

Controlled

Control value from React state when the selected option needs to coordinate with other application state.

Theme
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";import { useState } from "react";export function ControlledSelectDemo() {  const [value, setValue] = useState("light");  return (    <Select value={value} onValueChange={setValue} items={themeOptions}>      <SelectField>        <SelectLabel>Theme</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select theme" />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectList>          {themeOptions.map((item) => (            <SelectItem key={item.value} value={item.value}>              <SelectItemIndicator />              <SelectItemText>{item.label}</SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
const themeOptions = [  { label: "System", value: "system" },  { label: "Light", value: "light" },  { label: "Dark", value: "dark" },];

Clearable With Null Item

Include an item with value={null} when users should be able to clear the field from the same popup.

Theme
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";export function ClearableSelectDemo() {  return (    <Select items={clearableThemeOptions}>      <SelectField>        <SelectLabel>Theme</SelectLabel>        <SelectTrigger>          <SelectValue />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectList>          {clearableThemeOptions.map((item) => (            <SelectItem key={item.label} value={item.value}>              <SelectItemIndicator />              <SelectItemText>{item.label}</SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
const clearableThemeOptions = [  { label: "Select theme", value: null },  { label: "System", value: "system" },  { label: "Light", value: "light" },  { label: "Dark", value: "dark" },];

Object Values

Use itemToStringLabel and itemToStringValue when option values are objects.

Assignee
import {  Select,  SelectField,  SelectLabel,  SelectTrigger,  SelectValue,  SelectIcon,  SelectContent,  SelectList,  SelectItem,  SelectItemIndicator,  SelectItemText,} from "moduix";export function ObjectValuesSelectDemo() {  return (    <Select      items={assignees.map((assignee) => ({        value: assignee,        label: assignee.name,      }))}      itemToStringLabel={(assignee) => assignee.name}      itemToStringValue={(assignee) => assignee.id}    >      <SelectField>        <SelectLabel>Assignee</SelectLabel>        <SelectTrigger>          <SelectValue placeholder="Select assignee" />          <SelectIcon />        </SelectTrigger>      </SelectField>      <SelectContent>        <SelectList>          {assignees.map((assignee) => (            <SelectItem key={assignee.id} value={assignee}>              <SelectItemIndicator />              <SelectItemText>                <span className={styles.assigneeItemText}>                  <span className={styles.assigneeName}>{assignee.name}</span>                  <span className={styles.assigneeRole}>{assignee.role}</span>                </span>              </SelectItemText>            </SelectItem>          ))}        </SelectList>      </SelectContent>    </Select>  );}
.assigneeItemText {  display: flex;  min-width: 0;  flex-direction: column;  gap: var(--spacing-1);}.assigneeName {  overflow: hidden;  text-overflow: ellipsis;  white-space: nowrap;  font-weight: var(--weight-semibold);}.assigneeRole {  overflow: hidden;  text-overflow: ellipsis;  white-space: nowrap;  color: var(--color-muted-foreground);  font-size: var(--text-xs);  line-height: var(--line-height-text-xs);}
const assignees = [  { id: "u-1", name: "Leslie Alexander", role: "Product Manager" },  { id: "u-2", name: "Kathryn Murphy", role: "Marketing Lead" },  { id: "u-3", name: "Courtney Henry", role: "Design Systems" },  { id: "u-4", name: "Michael Foster", role: "Frontend Engineer" },];

On this page