RoadieRoadie
  • Home
  • Brand
  • Foundations
  • Content
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeBrandFoundations
Appearance

Components

  • Navigation

    • Breadcrumb
    • LinkBeta
    • NavigatorAlpha
    • Steps
    • Tabs
  • Layout

    • Accordion
    • CollapsibleBeta
    • PaneAlpha
    • ScrollArea
    • Separator
  • Actions

    • Button
    • IconButton
    • ToggleBeta
    • Toggle groupBeta
    • ToolbarBeta
  • Fields

    • EditorAlpha
    • Field
    • Fieldset
    • Input
    • Label
    • Number fieldBeta
    • OTP fieldBeta
    • Query fieldAlpha
    • Textarea
  • Choices

    • Autocomplete
    • CheckboxBeta
    • Combobox
    • Radio group
    • Select
    • SliderBeta
    • SwitchBeta
  • Date and time

    • CalendarBeta
    • CalendarTile
    • Countdown
    • Dashboard periodBeta
    • Date fieldBeta
    • Date pickerBeta
    • Date range pickerBeta
    • DateTime
    • Duration
    • Time fieldBeta
  • Collections

    • Card
    • Carousel
    • DataTableBeta
    • ListAlpha
    • Marquee
    • RecordGridAlpha
    • RecordTableAlpha
    • SortableAlpha
    • TableBeta
  • Overlays

    • Dialog
    • Drawer
    • MenuBeta
    • Popover
    • ToastBeta
    • Tooltip
  • Status

    • Badge
    • CalloutBeta
    • DeltaBeta
    • Empty state
    • MeterBeta
    • ProgressBeta
    • SkeletonBeta
    • SparklineBeta
    • StatTileBeta
  • Media & brand

    • AvatarBeta
    • Icon Tile
    • Image
    • Logo
    • PatternBeta
    • QR codeBeta
    • SpotIllustration
  • Text

    • Code
    • Highlight
    • KbdBeta
    • Mark
    • Prose

Select

Status
Stable
Added
Components 2.0.08 Apr 2026
Updated
Components 2.16.020 Oct 2026
Docs updated
19 Oct 2026

A dropdown for choosing from a list of options.

Import

import { Select } from '@oztix/roadie-components/select'

Examples

Default

Pass string children to Select.Item for automatic ItemText + ItemIndicator wrapping. Use Select.Content as a convenience wrapper for Portal + Positioner + Popup.

<Select defaultValue='powderfinger'>
<Select.Trigger>
<Select.Value placeholder='Pick a band' />
<Select.Icon />
</Select.Trigger>
<Select.Content>

Emphasis

normal is the default: a plain fill with a visible border, like a normal Button. subtle sits on a tinted fill. subtler has no fill or border at rest, for quiet controls inside something else, such as rows per page under a table.

<Select defaultValue='go-betweens'>
<Select.Trigger emphasis='normal'>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Content>

Sizes

<Select defaultValue='ball-park-music'>
<Select.Trigger size='sm'>
<Select.Value />
<Select.Icon />
</Select.Trigger>
<Select.Content>

States

normal and subtle triggers use is-interactive-field for state-based colour transitions: neutral at rest, accent on focus/open, danger when invalid. A subtler trigger uses is-interactive instead, so it behaves like a subtler Button: no fill at rest, a light fill on hover, press and while open, and a danger edge when invalid.

{/* Default */}
<Select defaultValue='goon-sax'>
<Select.Trigger>
<Select.Value />
<Select.Icon />
</Select.Trigger>

Composition

With Field

Wrap Select in Field for consistent layout, labels, and error handling. Field provides spacing, label wiring, and context flow automatically.

<Field required>
<Field.Label showIndicator>Favourite band</Field.Label>
<Select defaultValue='powderfinger'>
<Select.Trigger>
<Select.Value placeholder='Pick a band' />
<Select.Icon />

With Field and error

<Field invalid required>
<Field.Label showIndicator>Favourite band</Field.Label>
<Select>
<Select.Trigger>
<Select.Value placeholder='Pick a band' />
<Select.Icon />

With standalone label

Select also has its own Select.Label for standalone usage without a Field wrapper. Use showIndicator to show a required or optional indicator.

<Select defaultValue='powderfinger' required>
<div className='grid gap-1.5'>
<Select.Label showIndicator>Favourite band</Select.Label>
<Select.Trigger>
<Select.Value placeholder='Pick a band' />
<Select.Icon />

With groups

<Select defaultValue='powderfinger'>
<Select.Trigger>
<Select.Value placeholder='Pick a band' />
<Select.Icon />
</Select.Trigger>
<Select.Content>

With multiple selection

Set multiple to let people pick more than one option. The popup stays open between picks. The trigger lists the labels that fit and counts the rest, such as "Bee Gees, Custard +2", while screen readers hear every label. value, defaultValue and onValueChange then take an array.

<Field>
<Field.Label>Bands to see</Field.Label>
<Select multiple defaultValue={['bee-gees', 'custard']}>
<Select.Trigger>
<Select.Value placeholder='Pick bands' />
<Select.Icon />

Guidelines

When to use Select

Use Select when the list of options is known, short, and fixed. The user picks from a closed set. They don't need to search or type a custom value.

  • Country/state dropdowns (small set)
  • Status or category pickers
  • Sort-by controls
  • Any list under ~15 items where scanning is faster than typing

Choosing an emphasis

Save subtler for in-context controls

<Select.Trigger emphasis='subtler' size='sm' />

Do

Use subtler for a control that adjusts what is already on screen, such as rows per page, sort order or a chart's comparison, beside other subtler buttons. A subtler trigger takes only the width of its value and icon, where normal and subtle fill their container. If the value changes length, hold the width with the widest label so the controls beside it stay put.

Don’t

Don't use subtler for a select in a form. With no border at rest, it doesn't read as a field to fill in next to an Input.

When to use Combobox instead

If users are likely to search the list, the list is long (15+ items), or options load asynchronously, use Combobox.

QuestionSelectCombobox
Can the user type to filter?No (typeahead only)Yes
Is the list longer than ~15 items?AvoidPreferred
Are options loaded from an API?NoYes
Can the user enter a custom value?NoPossible
Does the user know roughly what they want?Maybe not. They browse.Yes. They search.

Keyboard behaviour

Select delegates all keyboard handling to Base UI:

  • Arrow keys navigate the list
  • Typeahead jumps to items by first letter(s)
  • Enter / Space confirms the highlighted item
  • Escape closes the popup

Accessibility

  • Always pair with Select.Label or an aria-label on the trigger.
  • Select.ItemIndicator provides a visible check for the selected item. Include it for clarity.
  • The popup locks scroll by default (modal={true}). Set modal={false} if the select appears in a non-blocking context like a toolbar.

API reference

Select

Base UI
inputRefRef<HTMLInputElement>

A ref to access the hidden input element.

namestring

Identifies the field when a form is submitted.

formstring

Identifies the form that owns the hidden input. Useful when the select is rendered outside the form.

autoCompletestring

Provides a hint to the browser for autofill. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete

idstring

The id of the Select.

requiredboolean

Whether the user must choose a value before submitting a form.

Defaults to false.

readOnlyboolean

Whether the user should be unable to choose a different option from the select popup.

Defaults to false.

disabledboolean

Whether the component should ignore user interaction.

Defaults to false.

multipleboolean

Whether multiple items can be selected.

Defaults to false.

highlightItemOnHoverboolean

Whether moving the pointer over items should highlight them. Disabling this prop allows CSS `:hover` to be differentiated from the `:focus` (`data-highlighted`) state.

Defaults to true.

defaultOpenboolean

Whether the select popup is initially open. To render a controlled select popup, use the `open` prop instead.

Defaults to false.

onOpenChange((open: boolean, eventDetails: SelectRootOpenChangeEventDetails) => void)

Event handler called when the select popup is opened or closed.

onOpenChangeComplete((open: boolean) => void)

Event handler called after any animations complete when the select popup is opened or closed.

openboolean

Whether the select popup is currently open.

modalboolean

Determines if the select enters a modal state when open. - `true`: user interaction is limited to the select: document page scroll is locked and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. On touch devices, a `true` modal blocks outside taps but leaves the page scrollable unless the popup spans nearly the full viewport width, matching native iOS behavior.

Defaults to true.

actionsRefRefObject<SelectRootActions | null>

A ref to imperative actions. - `unmount`: Ends the closing phase of the select after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the select completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the select imperatively when called.

itemsRecord<string, ReactNode> | readonly { label: ReactNode; value: any; }[] | readonly Group<any>[]

Data structure of the items rendered in the select popup. When specified, `<Select.Value>` renders the label of the selected item instead of the raw value. @example ```tsx const items = { sans: 'Sans-serif', serif: 'Serif', mono: 'Monospace', cursive: 'Cursive', }; <Select.Root items={items} /> ```

itemToStringLabel((itemValue: Value) => string)

When the item values are objects (`<Select.Item value={object}>`), this function converts the object value to a string representation for display in the trigger. If the shape of the object is `{ value, label }`, the label will be used automatically without needing to specify this prop.

itemToStringValue((itemValue: Value) => string)

When the item values are objects (`<Select.Item value={object}>`), this function converts the object value to a string representation for form submission. If the shape of the object is `{ value, label }`, the value will be used automatically without needing to specify this prop.

isItemEqualToValue((itemValue: Value, value: Value) => boolean)

Custom comparison logic used to determine if a select item value matches the current selected value. Useful when item values are objects without matching referentially. Defaults to `Object.is` comparison.

defaultValueSelectInputValue<Value, Multiple> | null

The uncontrolled value of the select when it's initially rendered. To render a controlled select, use the `value` prop instead.

valueSelectInputValue<Value, Multiple> | null

The value of the select. Use when controlled.

onValueChange((value: SelectOutputValue<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: SelectRootChangeEventDetails) => void)

Event handler called when the value of the select changes.

invalidboolean

Select.Content

classNamestring | ((state: SelectPopupState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectPopupProps

finalFocusboolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the select popup is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

Select.ErrorText

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.Group

Base UI

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.GroupLabel

Base UI

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.HelperText

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.Icon

Base UI

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.Item

Base UI
classNamestring | ((state: SelectItemState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectItemProps

valueany

A unique value that identifies this select item.

Defaults to null.

disabledboolean

Whether the component should ignore user interaction.

Defaults to false.

labelstring

Specifies the text label to use when the item is matched during keyboard text navigation. Defaults to the item text content if not provided.

Inherited from NonNativeButtonProps

nativeButtonboolean

Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.

Defaults to false.

Select.ItemIndicator

Base UI
classNamestring | ((state: SelectItemIndicatorState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectItemIndicatorProps

keepMountedboolean

Whether to keep the HTML element in the DOM when the item is not selected.

Select.ItemText

Base UI

No additional props. It forwards all standard HTML attributes to the underlying element.

Select.Label

Base UI
classNamestring | ((state: FieldRootState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

showIndicatorboolean

Select.Popup

Base UI
classNamestring | ((state: SelectPopupState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectPopupProps

finalFocusboolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the select popup is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

Select.Portal

Base UI
classNamestring | ((state: SelectPortalState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectPortalProps

containerHTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null

A parent element to render the portal element into.

Select.Positioner

Base UI
classNamestring | ((state: SelectPositionerState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectPositionerProps

alignItemWithTriggerboolean

Whether the positioner overlaps the trigger so the selected item's text is aligned with the trigger's value text. This only applies to mouse input and is automatically disabled if there is not enough space.

Defaults to true.

Inherited from UseAnchorPositioningSharedParameters

anchorElement | VirtualElement | RefObject<Element | null> | (() => Element | VirtualElement | null) | null

An element to position the popup against. By default, the popup will be positioned against the trigger.

positionMethod"absolute" | "fixed"

Determines which CSS `position` property to use.

Defaults to 'absolute'.

side"top" | "bottom" | "left" | "right" | "inline-end" | "inline-start"

Which side of the anchor element to align the popup against. May automatically change to avoid collisions.

Defaults to 'bottom'.

sideOffsetnumber | OffsetFunction

Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. @example ```jsx <Positioner sideOffset={({ side, align, anchor, positioner }) => { return side === 'top' || side === 'bottom' ? anchor.height : anchor.width; }} /> ```

Defaults to 0.

align"center" | "start" | "end"

How to align the popup relative to the specified side.

Defaults to 'center'.

alignOffsetnumber | OffsetFunction

Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. @example ```jsx <Positioner alignOffset={({ side, align, anchor, positioner }) => { return side === 'top' || side === 'bottom' ? anchor.width : anchor.height; }} /> ```

Defaults to 0.

collisionBoundaryBoundary

An element or a rectangle that delimits the area that the popup is confined to.

Defaults to 'clipping-ancestors'.

collisionPaddingPadding

Additional space to maintain from the edge of the collision boundary.

Defaults to 5.

stickyboolean

Whether to maintain the popup in the viewport after the anchor element was scrolled out of view.

Defaults to false.

arrowPaddingnumber

Minimum distance to maintain between the arrow and the edges of the popup. Use it to prevent the arrow element from hanging out of the rounded corners of a popup.

Defaults to 5.

disableAnchorTrackingboolean

Whether to disable the popup from tracking any layout shift of its positioning anchor.

Defaults to false.

collisionAvoidanceCollisionAvoidance

Determines how to handle collisions when positioning the popup. `side` controls overflow on the preferred placement axis (`top`/`bottom` or `left`/`right`): - `'flip'`: keep the requested side when it fits; otherwise try the opposite side (`top` and `bottom`, or `left` and `right`). - `'shift'`: never change side; keep the requested side and move the popup within the clipping boundary so it stays visible. - `'none'`: do not correct side-axis overflow. `align` controls overflow on the alignment axis (`start`/`center`/`end`): - `'flip'`: keep side, but swap `start` and `end` when the requested alignment overflows. - `'shift'`: keep side and requested alignment, then nudge the popup along the alignment axis to fit. - `'none'`: do not correct alignment-axis overflow. `fallbackAxisSide` controls fallback behavior on the perpendicular axis when the preferred axis cannot fit: - `'start'`: allow perpendicular fallback and try the logical start side first (`top` before `bottom`, or `left` before `right` in LTR). - `'end'`: allow perpendicular fallback and try the logical end side first (`bottom` before `top`, or `right` before `left` in LTR). - `'none'`: do not fallback to the perpendicular axis. When `side` is `'shift'`, explicitly setting `align` only supports `'shift'` or `'none'`. If `align` is omitted, it defaults to `'flip'`. @example ```jsx <Positioner collisionAvoidance={{ side: 'shift', align: 'shift', fallbackAxisSide: 'none', }} /> ```

Select.ScrollDownArrow

Base UI
classNamestring | ((state: SelectScrollDownArrowState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectScrollDownArrowProps

keepMountedboolean

Whether to keep the HTML element in the DOM while the select popup is not scrollable.

Defaults to false.

Select.ScrollUpArrow

Base UI
classNamestring | ((state: SelectScrollUpArrowState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectScrollUpArrowProps

keepMountedboolean

Whether to keep the HTML element in the DOM while the select popup is not scrollable.

Defaults to false.

Select.Trigger

Base UI
classNamestring | ((state: SelectTriggerState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

emphasis"normal" | "subtle" | "subtler" | null

`subtler` drops the fill and border for quiet in-context controls, such as rows per page.

Defaults to 'normal'.

size"sm" | "md" | "lg" | null

Defaults to 'md'.

intent"neutral" | "brand" | "brand-secondary" | "brand-blue" | "brand-orange" | "brand-purple" | "accent" | "danger" | "success" | "warning" | "info" | null
Deprecated

Deprecated: Form controls take their colour from state; `is-interactive-field` handles it. Will be removed in v3.0.0.

Inherited from SelectTriggerProps

disabledboolean

Whether the component should ignore user interaction.

Inherited from NonNativeButtonProps

nativeButtonboolean

Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `true` if the rendered element is a native button.

Defaults to false.

Select.Value

Base UI
classNamestring | ((state: SelectValueState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from SelectValueProps

childrenReactNode | ((value: any) => ReactNode)

Accepts a function that returns a `ReactNode` to format the selected value. Treat the value as read-only: in `multiple` mode it may be a shared frozen array when nothing is selected. @example ```tsx <Select.Value> {(value: string | null) => value ? labels[value] : 'No value'} </Select.Value> ```

placeholderReactNode

The placeholder value to display when no value is selected. This is overridden by `children` if specified, or by a null item's label in `items`.

Previous page← Radio groupNext pageSlider →