Select

A custom dropdown for picking one value from a list, from @clerk/headless. It is a headless primitive: it supplies value and open state, portalling, floating-ui positioning (with optional native-<select>-style item alignment), keyboard navigation with typeahead, and ARIA wiring, but ships no styles — you bring your own CSS by targeting the data-* attributes each part emits.

Example

The demo below is intentionally unstyled — it renders the raw primitive so you can see its behavior and ARIA wiring. Open it and pick an option (arrow keys, typeahead, and Enter all work); the trigger reflects the selected value.

Usage

import { Select } from '@clerk/headless/select';

<Select.Root>
  <Select.Trigger>
    <Select.Value placeholder='Choose a fruit…' />
  </Select.Trigger>
  <Select.Portal>
    <Select.Positioner>
      <Select.Popup>
        <Select.Option value='apple'>Apple</Select.Option>
        <Select.Option value='banana'>Banana</Select.Option>
        <Select.Option
          value='cherry'
          disabled
        >
          Cherry
        </Select.Option>
      </Select.Popup>
    </Select.Positioner>
  </Select.Portal>
</Select.Root>;

Controlled

const [value, setValue] = useState('apple');

<Select.Root
  value={value}
  onValueChange={setValue}
>
  {/* trigger + portal */}
</Select.Root>;

Parts

PartDefault ElementDescription
Select.Rootnone (context)Owns value and open state plus positioning
Select.Trigger<button>Toggles the dropdown; acts as the positioning anchor
Select.Value<span>Displays the selected option's label, or the placeholder
Select.Portalnone (portal)Portals its children; renders nothing until mounted
Select.Positioner<div>Floating-positioned container (role="listbox")
Select.Popup<div>Visual wrapper for the option list
Select.Option<button>A selectable option (role="option")
Select.Arrow<svg>Optional arrow (only positioned when alignItemWithTrigger={false})

All rendered parts accept a render prop for polymorphic rendering and standard HTML attributes for their default element. Select.Arrow accepts floating-ui FloatingArrow props. Select.Portal is optional.

Props

Select.Root

PropTypeDefaultDescription
valuestringControlled selected value
defaultValuestringInitial selected value (uncontrolled)
onValueChange(value: string) => voidCalled when the selection changes
openbooleanControlled open state
defaultOpenbooleanfalseInitial open state (uncontrolled)
onOpenChange(open: boolean) => voidCalled when the open state changes
itemsSelectItem[]{ label, value }[] used to resolve the selected label early
alignItemWithTriggerbooleantrueOverlay the selected item on the trigger (native-<select> style)
placementPlacement'bottom-start'Placement when not aligning to the item
sideOffsetnumber4Gap in px (applies only when alignItemWithTrigger={false})

SelectItem is { label: string; value: string } — disabling is done per-Option.

Select.Value

PropTypeDefaultDescription
placeholderReactNodeRendered when no value is selected

Select.Option

PropTypeDefaultDescription
valuestring— (required)The option's value
labelstringfalls back to valueDisplay label, also used for typeahead
disabledbooleanPrevent selection (stays keyboard-focusable)

Select.Trigger, Select.Positioner, and Select.Popup take no additional props beyond standard HTML attributes for their default element. Select.Portal accepts root.

Styling

Each part emits data-* attributes you can target with any CSS solution:

AttributeApplies ToDescription
data-openTrigger, PopupPresent when the dropdown is open
data-closedTrigger, PopupPresent when closed (Popup stays mounted to exit)
data-selectedOptionPresent on the currently selected option
data-activeOptionPresent on the keyboard-highlighted option
data-disabledOptionPresent on a disabled option
data-sidePositioner, ArrowResolved side: top / bottom / left / right
data-starting-stylePopupPresent on the entering frame
data-ending-stylePopupPresent during the exit animation

The positioner exposes anchor/viewport geometry as CSS variables (and sets an inline max-height to the available viewport height):

VariableDescription
--cl-anchor-widthTrigger width
--cl-anchor-heightTrigger height
--cl-available-widthAvailable width between the anchor and viewport edge
--cl-available-heightAvailable height between the anchor and viewport edge
--cl-transform-origintransform-origin pointing back toward the anchor