# Select (/docs/select)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/select" />

## Choosing the right component [#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 &#x2A;*`Select`** for canonical form pickers with fixed options.
* Choose &#x2A;*`Combobox`** when users should search/filter predefined options via text input.
* Choose &#x2A;*`Autocomplete`** when users may enter arbitrary values beyond suggested options.

## Basic [#basic]

<Preview cssProperties="selectPlaygroundCssProperties">
  <SelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          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" },
          ];
        `}
  </Preview.Data>

  <Preview.CSSProperties>
    {(context) => <SelectCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <SelectCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#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.

```text
Select
├─ SelectField
│  ├─ SelectLabel
│  └─ SelectTrigger
│     ├─ SelectValue
│     └─ SelectIcon
└─ SelectContent
   ├─ service slots (internal): portal, backdrop, positioner, arrow
   ├─ SelectScrollUpArrow / SelectScrollDownArrow (optional)
   └─ SelectList
      └─ SelectItem[value]
         ├─ SelectItemIndicator
         └─ SelectItemText
```

```tsx
<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>
```

| Part            | Role                                                                                                            |
| --------------- | --------------------------------------------------------------------------------------------------------------- |
| `Select`        | Root state machine. Controls selected value(s), popup open state, keyboard navigation, and item string mapping. |
| `SelectField`   | Field wrapper for label and trigger. Keeps form semantics and visible control layout together.                  |
| `SelectTrigger` | Interactive button that opens/closes the popup and exposes open/disabled state attributes.                      |
| `SelectValue`   | Value renderer inside the trigger. Shows placeholder, selected label, or a custom function output.              |
| `SelectContent` | Popup surface that hosts list/scroll controls and configures internal service layers and positioning props.     |
| `SelectItem`    | One 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 [#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:

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

## Examples [#examples]

### Indicator Right With Icon [#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.

<Preview>
  <IndicatorRightSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customTriggerIcon {
            color: var(--color-foreground);
          }

          .statusIcon {
            color: var(--color-muted-foreground);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const fruits = [
            { label: "Apple", value: "apple" },
            { label: "Banana", value: "banana" },
            { label: "Mango", value: "mango" },
          ];
        `}
  </Preview.Data>
</Preview>

### Grouped [#grouped]

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

<Preview>
  <GroupedSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          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])),
          );
        `}
  </Preview.Data>
</Preview>

### Animated [#animated]

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

<Preview>
  <AnimatedSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .animatedContent {
            --select-transition: 180ms var(--ease-out);
            --select-scale: 0.96;
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const fruits = [
            { label: "Apple", value: "apple" },
            { label: "Banana", value: "banana" },
            { label: "Mango", value: "mango" },
          ];
        `}
  </Preview.Data>
</Preview>

### Multiple [#multiple]

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

<Preview>
  <MultipleSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const languages = {
            javascript: "JavaScript",
            python: "Python",
            rust: "Rust",
            typescript: "TypeScript",
          };

          const languageValues = Object.keys(languages);
        `}
  </Preview.Data>
</Preview>

### Custom Styles [#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.

<Preview>
  <CustomStylesSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .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);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const fruits = [
            { label: "Apple", value: "apple" },
            { label: "Banana", value: "banana" },
            { label: "Mango", value: "mango" },
          ];
        `}
  </Preview.Data>
</Preview>

### Controlled [#controlled]

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

<Preview>
  <ControlledSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const themeOptions = [
            { label: "System", value: "system" },
            { label: "Light", value: "light" },
            { label: "Dark", value: "dark" },
          ];
        `}
  </Preview.Data>
</Preview>

### Clearable With Null Item [#clearable-with-null-item]

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

<Preview>
  <ClearableSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const clearableThemeOptions = [
            { label: "Select theme", value: null },
            { label: "System", value: "system" },
            { label: "Light", value: "light" },
            { label: "Dark", value: "dark" },
          ];
        `}
  </Preview.Data>
</Preview>

### Object Values [#object-values]

Use `itemToStringLabel` and `itemToStringValue` when option values are objects.

<Preview>
  <ObjectValuesSelectExample />

  <Preview.Code>
    {`
          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>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .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);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          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" },
          ];
        `}
  </Preview.Data>
</Preview>
