# SelectPrimitive

`SelectPrimitive` is a set of styled wrappers around
[Base UI's Select](https://base-ui.com/react/components/select) parts,
pre-themed with Atlantis design tokens. It exposes every building block as a
named sub-component so consumers compose their own field structure — no label,
error, or description abstraction is included.

> **⚠️ Experimental** — This is a primitive building block. It does not carry
> the same stability guarantees as promoted Atlantis components. Reach out to
> UXF before adopting it in product code.

## Design & usage guidelines

Use `SelectPrimitive` when you need a composable, fully-styled select that goes
beyond what the native `<select>` element supports — e.g. custom item layouts,
option groups with separators, or accessible keyboard navigation baked in
through Base UI.

For everyday form selects, prefer the standard `Select` component.

### Composition

Because `SelectPrimitive` provides no field abstraction, consumers wire up
labels and other field structure themselves:

```tsx
<label htmlFor="my-select">Fruit</label>
<SelectPrimitive.Root value={value} onValueChange={setValue}>
  <SelectPrimitive.Trigger id="my-select">
    <SelectPrimitive.Value placeholder="Select an option" />
  </SelectPrimitive.Trigger>
  <SelectPrimitive.Portal>
    <SelectPrimitive.Positioner>
      <SelectPrimitive.Popup>
        <SelectPrimitive.List>
          <SelectPrimitive.Item value="apple">
            <SelectPrimitive.ItemText>Apple</SelectPrimitive.ItemText>
            <SelectPrimitive.ItemIndicator />
          </SelectPrimitive.Item>
          <SelectPrimitive.Item value="banana">
            <SelectPrimitive.ItemText>Banana</SelectPrimitive.ItemText>
            <SelectPrimitive.ItemIndicator />
          </SelectPrimitive.Item>
        </SelectPrimitive.List>
      </SelectPrimitive.Popup>
    </SelectPrimitive.Positioner>
  </SelectPrimitive.Portal>
</SelectPrimitive.Root>
```

### Placeholder

Pass a `placeholder` string to `SelectPrimitive.Value` to show hint text inside
the trigger when no value is selected. It is replaced by the selected value once
the user makes a choice.

### Dropdown

The dropdown popup mirrors the `Menu` component's visual style — same border,
shadow, border-radius, and item hover/highlight states. The popup is always at
least as wide as the trigger.

A checkmark indicator (`SelectPrimitive.ItemIndicator`) appears on the currently
selected item. Place it inside each `SelectPrimitive.Item` to opt in.

## States

### Basic

```tsx
<label htmlFor="basic-select">Fruit</label>
<SelectPrimitive.Root value={value} onValueChange={setValue}>
  <SelectPrimitive.Trigger id="basic-select">
    <SelectPrimitive.Value placeholder="Select an option" />
  </SelectPrimitive.Trigger>
  <SelectPrimitive.Portal>
    <SelectPrimitive.Positioner>
      <SelectPrimitive.Popup>
        <SelectPrimitive.List>
          <SelectPrimitive.Item value="apple">
            <SelectPrimitive.ItemText>Apple</SelectPrimitive.ItemText>
            <SelectPrimitive.ItemIndicator />
          </SelectPrimitive.Item>
          <SelectPrimitive.Item value="banana">
            <SelectPrimitive.ItemText>Banana</SelectPrimitive.ItemText>
            <SelectPrimitive.ItemIndicator />
          </SelectPrimitive.Item>
        </SelectPrimitive.List>
      </SelectPrimitive.Popup>
    </SelectPrimitive.Positioner>
  </SelectPrimitive.Portal>
</SelectPrimitive.Root>
```

### Disabled

Pass `disabled` to `SelectPrimitive.Root` to prevent interaction. The trigger
border, background, and text colour all shift to the disabled palette.

### Option grouping

Use `SelectPrimitive.Group` and `SelectPrimitive.GroupLabel` to organise items
under labelled sections, and `SelectPrimitive.Separator` to add a horizontal
rule between groups.

```tsx
<SelectPrimitive.Root value={value} onValueChange={setValue}>
  <SelectPrimitive.Trigger id="grouped-select">
    <SelectPrimitive.Value placeholder="Choose produce" />
  </SelectPrimitive.Trigger>
  <SelectPrimitive.Portal>
    <SelectPrimitive.Positioner>
      <SelectPrimitive.Popup>
        <SelectPrimitive.List>
          <SelectPrimitive.Group>
            <SelectPrimitive.GroupLabel>Fruits</SelectPrimitive.GroupLabel>
            <SelectPrimitive.Item value="apple">
              <SelectPrimitive.ItemText>Apple</SelectPrimitive.ItemText>
              <SelectPrimitive.ItemIndicator />
            </SelectPrimitive.Item>
          </SelectPrimitive.Group>

          <SelectPrimitive.Separator />

          <SelectPrimitive.Group>
            <SelectPrimitive.GroupLabel>Vegetables</SelectPrimitive.GroupLabel>
            <SelectPrimitive.Item value="carrot">
              <SelectPrimitive.ItemText>Carrot</SelectPrimitive.ItemText>
              <SelectPrimitive.ItemIndicator />
            </SelectPrimitive.Item>
          </SelectPrimitive.Group>
        </SelectPrimitive.List>
      </SelectPrimitive.Popup>
    </SelectPrimitive.Positioner>
  </SelectPrimitive.Portal>
</SelectPrimitive.Root>
```


## Configuration

### Controlled vs uncontrolled

`SelectPrimitive.Root` supports both controlled (`value` + `onValueChange`) and
uncontrolled (`defaultValue`) usage.

### Generic value type

`SelectPrimitive.Root` is generic over the value type — the `value` and
`onValueChange` props stay fully typed without extra casting.

### Keyboard navigation

Base UI provides full keyboard support out of the box:

* **Arrow Up / Down** — move between items
* **Enter / Space** — select the highlighted item
* **Escape** — close the dropdown without changing the value
* **Home / End** — jump to the first / last item

### Form integration

Pass `name` and `required` to `SelectPrimitive.Root` to participate in standard
HTML form submission. The value is serialised via a hidden `<input>` managed by
Base UI.

## Sub-components

| Sub-component                   | Purpose                                                                                                        |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `SelectPrimitive.Root`          | Provides state, value, and open/close control. No DOM element of its own.                                      |
| `SelectPrimitive.Trigger`       | The button that opens the dropdown. Renders the chevron icon automatically.                                    |
| `SelectPrimitive.Value`         | Displays the selected value (or placeholder) inside the trigger.                                               |
| `SelectPrimitive.Portal`        | Renders the popup in a portal outside the current DOM subtree.                                                 |
| `SelectPrimitive.Positioner`    | Positions the popup relative to the trigger using floating-ui.                                                 |
| `SelectPrimitive.Popup`         | The styled dropdown container. Sized to at least the trigger's width.                                          |
| `SelectPrimitive.List`          | Scrollable list container inside the popup.                                                                    |
| `SelectPrimitive.Item`          | An individual selectable option. Accepts `value`, `disabled`, and `label`.                                     |
| `SelectPrimitive.ItemText`      | The visible text label inside an item.                                                                         |
| `SelectPrimitive.ItemIndicator` | A checkmark shown only on the selected item. Renders an Atlantis icon by default; pass `children` to override. |
| `SelectPrimitive.Group`         | Groups items together under a common label.                                                                    |
| `SelectPrimitive.GroupLabel`    | The heading rendered above a group.                                                                            |
| `SelectPrimitive.Separator`     | A horizontal rule between groups.                                                                              |


## Props

### Web

#### Portal

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectPortalState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `container` | `HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot>` | No | — | A parent element to render the portal element into. |
| `ref` | `Ref<HTMLDivElement>` | No | — | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (... |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectPortalState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectPortalState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### Root

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `actionsRef` | `RefObject<SelectRootActions>` | No | — | A ref to imperative actions. - `unmount`: Manually unmounts the select. Call this after any externally controlled clo... |
| `autoComplete` | `string` | No | — | Provides a hint to the browser for autofill. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attribu... |
| `defaultOpen` | `boolean` | No | `false` | Whether the select popup is initially open.  To render a controlled select popup, use the `open` prop instead. |
| `defaultValue` | `SelectValueType<Value, Multiple>` | No | — | The uncontrolled value of the select when it's initially rendered.  To render a controlled select, use the `value` pr... |
| `disabled` | `boolean` | No | `false` | Whether the component should ignore user interaction. |
| `form` | `string` | No | — | Identifies the form that owns the hidden input. Useful when the select is rendered outside the form. |
| `highlightItemOnHover` | `boolean` | No | `true` | Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differenti... |
| `id` | `string` | No | — | The id of the Select. |
| `inputRef` | `Ref<HTMLInputElement>` | No | — | A ref to access the hidden input element. |
| `isItemEqualToValue` | `(itemValue: Value, value: Value) => boolean` | No | — | Custom comparison logic used to determine if a select item value matches the current selected value. Useful when item... |
| `items` | `Record<string, ReactNode> | readonly { label: ReactNode; value: any; }[] | readonly Group<any>[]` | No | — | Data structure of the items rendered in the select popup. When specified, `<Select.Value>` renders the label of the s... |
| `itemToStringLabel` | `(itemValue: Value) => string` | No | — | When the item values are objects (`<Select.Item value={object}>`), this function converts the object value to a strin... |
| `itemToStringValue` | `(itemValue: Value) => string` | No | — | When the item values are objects (`<Select.Item value={object}>`), this function converts the object value to a strin... |
| `modal` | `boolean` | No | `true` | Determines if the select enters a modal state when open. - `true`: user interaction is limited to the select: documen... |
| `multiple` | `boolean` | No | `false` | Whether multiple items can be selected. |
| `name` | `string` | No | — | Identifies the field when a form is submitted. |
| `onOpenChange` | `(open: boolean, eventDetails: SelectRootChangeEventDetails) => void` | No | — | Event handler called when the select popup is opened or closed. |
| `onOpenChangeComplete` | `(open: boolean) => void` | No | — | Event handler called after any animations complete when the select popup is opened or closed. |
| `onValueChange` | `(value: SelectValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void` | No | — | Event handler called when the value of the select changes. |
| `open` | `boolean` | No | — | Whether the select popup is currently open. |
| `readOnly` | `boolean` | No | `false` | Whether the user should be unable to choose a different option from the select popup. |
| `required` | `boolean` | No | `false` | Whether the user must choose a value before submitting a form. |
| `value` | `SelectValueType<Value, Multiple>` | No | — | The value of the select. Use when controlled. |

#### SelectPrimitiveGroup

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectGroupState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectGroupState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectGroupState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveGroupLabel

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectGroupLabelState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectGroupLabelState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectGroupLabelState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveItem

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectItemState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `disabled` | `boolean` | No | `false` | Whether the component should ignore user interaction. |
| `label` | `string` | No | — | Specifies the text label to use when the item is matched during keyboard text navigation.  Defaults to the item text ... |
| `nativeButton` | `boolean` | No | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if ... |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectItemState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectItemState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |
| `value` | `any` | No | `null` | A unique value that identifies this select item. |

#### SelectPrimitiveItemIndicator

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectItemIndicatorState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `keepMounted` | `boolean` | No | — | Whether to keep the HTML element in the DOM when the item is not selected. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectItemIndicatorState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectItemIndicatorState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveItemText

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectItemTextState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectItemTextState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectItemTextState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveList

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectListState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectListState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectListState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitivePopup

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectPopupState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `finalFocus` | `boolean | RefObject<HTMLElement> | ((closeType: InteractionType) => boolean | void | HTMLElement)` | No | — | Determines the element to focus when the select popup is closed.  - `false`: Do not move focus. - `true`: Move focus ... |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectPopupState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectPopupState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitivePositioner

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `align` | `Align` | No | `'center'` | How to align the popup relative to the specified side. |
| `alignItemWithTrigger` | `boolean` | No | `false` | Whether the positioner overlaps the trigger so the selected item's text is aligned with the trigger's value text. Thi... |
| `alignOffset` | `OffsetFunction | number` | No | `0` | Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dim... |
| `anchor` | `Element | RefObject<Element> | VirtualElement | (() => Element | VirtualElement)` | No | — | An element to position the popup against. By default, the popup will be positioned against the trigger. |
| `arrowPadding` | `number` | No | `5` | Minimum distance to maintain between the arrow and the edges of the popup.  Use it to prevent the arrow element from ... |
| `className` | `string | ((state: SelectPositionerState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `collisionAvoidance` | `CollisionAvoidance` | No | — | Determines how to handle collisions when positioning the popup.  `side` controls overflow on the preferred placement ... |
| `collisionBoundary` | `Boundary` | No | `'clipping-ancestors'` | An element or a rectangle that delimits the area that the popup is confined to. |
| `collisionPadding` | `Padding` | No | `5` | Additional space to maintain from the edge of the collision boundary. |
| `disableAnchorTracking` | `boolean` | No | `false` | Whether to disable the popup from tracking any layout shift of its positioning anchor. |
| `positionMethod` | `"absolute" | "fixed"` | No | `'absolute'` | Determines which CSS `position` property to use. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectPositionerState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `side` | `Side` | No | `'bottom'` | Which side of the anchor element to align the popup against. May automatically change to avoid collisions. |
| `sideOffset` | `OffsetFunction | number` | No | `6` | Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the di... |
| `sticky` | `boolean` | No | `false` | Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. |
| `style` | `CSSProperties | ((state: SelectPositionerState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveSeparator

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string` | No | — |  |
| `orientation` | `Orientation` | No | `'horizontal'` | The orientation of the separator. |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SeparatorState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SeparatorState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveTrigger

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `className` | `string | ((state: SelectTriggerState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `disabled` | `boolean` | No | — | Whether the component should ignore user interaction. |
| `nativeButton` | `boolean` | No | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if ... |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectTriggerState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectTriggerState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |

#### SelectPrimitiveValue

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `children` | `ReactNode | ((value: any) => ReactNode)` | No | — | Accepts a function that returns a `ReactNode` to format the selected value. @example ```tsx <Select.Value>   {(value:... |
| `className` | `string | ((state: SelectValueState) => string)` | No | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
| `placeholder` | `ReactNode` | No | — | The placeholder value to display when no value is selected. This is overridden by `children` if specified, or by a nu... |
| `render` | `ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SelectValueState>` | No | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accep... |
| `style` | `CSSProperties | ((state: SelectValueState) => CSSProperties)` | No | — | Style applied to the element, or a function that returns a style object based on the component's state. |
