Components
Select

Select

Displays a list of options for the user to pick from.

Anatomy

To set up the select correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Examples

Learn how to use the Select component in your project. Let's take a look at the most basic example:

import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const Basic = () => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })

  return (
    <Select.Root collection={collection}>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
        <Select.ClearTrigger>Clear</Select.ClearTrigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item} item={item}>
                  <Select.ItemText>{item}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Portal>
      <Select.HiddenSelect />
    </Select.Root>
  )
}

Advanced Customization

For advanced customizations and item properties like disabled:

import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const Advanced = () => {
  const collection = createListCollection({
    items: [
      { label: 'React', value: 'react' },
      { label: 'Solid', value: 'solid' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte', disabled: true },
    ],
  })

  return (
    <Select.Root collection={collection}>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
        <Select.ClearTrigger>Clear</Select.ClearTrigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item.value} item={item}>
                  <Select.ItemText>{item.label}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Portal>
      <Select.HiddenSelect />
    </Select.Root>
  )
}

Multiple Selection

To enable multiple item selection:

import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const Multiple = () => {
  const collection = createListCollection({
    items: [
      { label: 'React', value: 'react' },
      { label: 'Solid', value: 'solid' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte', disabled: true },
    ],
  })
  return (
    <Select.Root collection={collection} multiple>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
        <Select.ClearTrigger>Clear</Select.ClearTrigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item.value} item={item}>
                  <Select.ItemText>{item.label}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Portal>
      <Select.HiddenSelect />
    </Select.Root>
  )
}

Controlled Component

For scenarios where you want to control the Select component's state:

import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
import { useState } from 'react'

interface Item {
  label: string
  value: string
  disabled?: boolean
}

export const Controlled = () => {
  const [_, setSelectedItems] = useState<Item[]>([])

  const collection = createListCollection<Item>({
    items: [
      { label: 'React', value: 'react' },
      { label: 'Solid', value: 'solid' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte', disabled: true },
    ],
  })

  return (
    <Select.Root collection={collection} onValueChange={(e) => setSelectedItems(e.items)}>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
        <Select.ClearTrigger>Clear</Select.ClearTrigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item.value} item={item}>
                  <Select.ItemText>{item.label}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Portal>
      <Select.HiddenSelect />
    </Select.Root>
  )
}

Usage with a Form Library

See how to use the Select component with popular form libraries:

import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
import { type SubmitHandler, useForm } from 'react-hook-form'

interface Inputs {
  framework: string
}

export const FormLibrary = () => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
  const { register, handleSubmit } = useForm<Inputs>()

  const onSubmit: SubmitHandler<Inputs> = (data) => window.alert(JSON.stringify(data))

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Select.Root collection={collection}>
        <Select.Label>Framework</Select.Label>
        <Select.HiddenSelect {...register('framework')} />
        <Select.Control>
          <Select.Trigger>
            <Select.ValueText placeholder="Select a Framework" />
            <Select.Indicator>
              <ChevronDownIcon />
            </Select.Indicator>
          </Select.Trigger>
          <Select.ClearTrigger>Clear</Select.ClearTrigger>
        </Select.Control>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item} item={item}>
                  <Select.ItemText>{item}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Select.Root>
      <button type="submit">Submit</button>
    </form>
  )
}

Using the Field Component

The Field component helps manage form-related state and accessibility attributes of a select. It includes handling ARIA labels, helper text, and error text to ensure proper accessibility.

import { Field } from '@ark-ui/react/field'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const WithField = (props: Field.RootProps) => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })

  return (
    <Field.Root {...props}>
      <Select.Root collection={collection}>
        <Select.Label>Label</Select.Label>
        <Select.Control>
          <Select.Trigger>
            <Select.ValueText placeholder="Select a Framework" />
            <Select.Indicator>
              <ChevronDownIcon />
            </Select.Indicator>
          </Select.Trigger>
        </Select.Control>
        <Select.Positioner>
          <Select.Content>
            {collection.items.map((item) => (
              <Select.Item key={item} item={item}>
                <Select.ItemText>{item}</Select.ItemText>
                <Select.ItemIndicator>✓</Select.ItemIndicator>
              </Select.Item>
            ))}
          </Select.Content>
        </Select.Positioner>
        <Select.HiddenSelect />
      </Select.Root>
      <Field.HelperText>Additional Info</Field.HelperText>
      <Field.ErrorText>Error Info</Field.ErrorText>
    </Field.Root>
  )
}

Using the Root Provider

The RootProvider component provides a context for the select. It accepts the value of the useSelect hook. You can leverage it to access the component state and methods from outside the select.

import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection, useSelect } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const RootProvider = () => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })

  const select = useSelect({ collection: collection })

  return (
    <>
      <button onClick={() => select.focus()}>Focus</button>

      <Select.RootProvider value={select}>
        <Select.Label>Framework</Select.Label>
        <Select.Control>
          <Select.Trigger>
            <Select.ValueText placeholder="Select a Framework" />
            <Select.Indicator>
              <ChevronDownIcon />
            </Select.Indicator>
          </Select.Trigger>
          <Select.ClearTrigger>Clear</Select.ClearTrigger>
        </Select.Control>
        <Portal>
          <Select.Positioner>
            <Select.Content>
              <Select.ItemGroup>
                <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
                {collection.items.map((item) => (
                  <Select.Item key={item} item={item}>
                    <Select.ItemText>{item}</Select.ItemText>
                    <Select.ItemIndicator>✓</Select.ItemIndicator>
                  </Select.Item>
                ))}
              </Select.ItemGroup>
            </Select.Content>
          </Select.Positioner>
        </Portal>
        <Select.HiddenSelect />
      </Select.RootProvider>
    </>
  )
}

If you're using the RootProvider component, you don't need to use the Root component.

API Reference

Root

PropDefaultType
collection
ListCollection<T>

The collection of items

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
closeOnSelecttrue
boolean

Whether the select should close after an item is selected

compositetrue
boolean

Whether the select is a composed with other composite widgets like tabs or combobox

defaultOpen
boolean

The initial open state of the select when it is first rendered. Use when you do not need to control its open state.

defaultValue
string[]

The initial value of the select when it is first rendered. Use when you do not need to control the state of the select.

deselectable
boolean

Whether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection

disabled
boolean

Whether the select is disabled

form
string

The associate form of the underlying select.

highlightedValue
string

The key of the highlighted item

id
string

The unique identifier of the machine.

ids
Partial<{ root: string content: string control: string trigger: string clearTrigger: string label: string hiddenSelect: string positioner: string item(id: string | number): string itemGroup(id: string | number): string itemGroupLabel(id: string | number): string }>

The ids of the elements in the select. Useful for composition.

immediate
boolean

Whether to synchronize the present change immediately or defer it to the next frame

invalid
boolean

Whether the select is invalid

lazyMountfalse
boolean

Whether to enable lazy mounting

loopFocusfalse
boolean

Whether to loop the keyboard navigation through the options

multiple
boolean

Whether to allow multiple selection

name
string

The `name` attribute of the underlying select.

onExitComplete
() => void

Function called when the animation ends in the closed state

onFocusOutside
(event: FocusOutsideEvent) => void

Function called when the focus is moved outside the component

onHighlightChange
(details: HighlightChangeDetails<T>) => void

The callback fired when the highlighted item changes.

onInteractOutside
(event: InteractOutsideEvent) => void

Function called when an interaction happens outside the component

onOpenChange
(details: OpenChangeDetails) => void

Function called when the popup is opened

onPointerDownOutside
(event: PointerDownOutsideEvent) => void

Function called when the pointer is pressed down outside the component

onValueChange
(details: ValueChangeDetails<T>) => void

The callback fired when the selected item changes.

open
boolean

Whether the select menu is open

positioning
PositioningOptions

The positioning options of the menu.

present
boolean

Whether the node is present (controlled by the user)

readOnly
boolean

Whether the select is read-only

required
boolean

Whether the select is required

scrollToIndexFn
(details: ScrollToIndexDetails) => void

Function to scroll to a specific index

unmountOnExitfalse
boolean

Whether to unmount on exit.

value
string[]

The keys of the selected items

Data AttributeValue
[data-scope]select
[data-part]root
[data-invalid]Present when invalid
[data-readonly]Present when read-only

ClearTrigger

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]clear-trigger
[data-invalid]Present when invalid

Content

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]content
[data-state]"open" | "closed"
[data-placement]The placement of the content
[data-activedescendant]

Control

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]control
[data-state]"open" | "closed"
[data-focus]Present when focused
[data-disabled]Present when disabled
[data-invalid]Present when invalid

HiddenSelect

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Indicator

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]indicator
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-invalid]Present when invalid
[data-readonly]Present when read-only

ItemGroupLabel

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

ItemGroup

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]item-group
[data-disabled]Present when disabled

ItemIndicator

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]item-indicator
[data-state]"checked" | "unchecked"

Item

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
item
any

The item to render

persistFocus
boolean

Whether hovering outside should clear the highlighted state

Data AttributeValue
[data-scope]select
[data-part]item
[data-value]The value of the item
[data-state]"checked" | "unchecked"
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled

ItemText

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]item-text
[data-state]"checked" | "unchecked"
[data-disabled]Present when disabled
[data-highlighted]Present when highlighted

Label

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]label
[data-disabled]Present when disabled
[data-invalid]Present when invalid
[data-readonly]Present when read-only

List

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Positioner

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

RootProvider

PropDefaultType
value
UseSelectReturn<T>

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
immediate
boolean

Whether to synchronize the present change immediately or defer it to the next frame

lazyMountfalse
boolean

Whether to enable lazy mounting

onExitComplete
() => void

Function called when the animation ends in the closed state

present
boolean

Whether the node is present (controlled by the user)

unmountOnExitfalse
boolean

Whether to unmount on exit.

Trigger

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]select
[data-part]trigger
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-invalid]Present when invalid
[data-readonly]Present when read-only
[data-placement]The placement of the trigger
[data-placeholder-shown]Present when placeholder is shown

ValueText

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
placeholder
string

Text to display when no value is selected.

Data AttributeValue
[data-scope]select
[data-part]value-text
[data-disabled]Present when disabled
[data-invalid]Present when invalid
[data-focus]Present when focused

Accessibility

Complies with the Listbox WAI-ARIA design pattern.

Keyboard Support

KeyDescription
Space
When focus is on trigger, opens the select and focuses the first selected item.
When focus is on the content, selects the highlighted item.
Enter
When focus is on trigger, opens the select and focuses the first selected item.
When focus is on content, selects the focused item.
ArrowDown
When focus is on trigger, opens the select.
When focus is on content, moves focus to the next item.
ArrowUp
When focus is on trigger, opens the select.
When focus is on content, moves focus to the previous item.
Esc
Closes the select and moves focus to trigger.
A-Za-z
When focus is on trigger, selects the item whose label starts with the typed character.
When focus is on the listbox, moves focus to the next item with a label that starts with the typed character.