Select
Displays a list of options for the user to pick from.
Displays a list of options for the user to pick from.
To set up the select correctly, you’ll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Learn how to use the Select component in your project. Let’s take a look at
the most basic example:
import { ChevronDownIcon } from 'lucide-react'
import { Portal, Select } from '@ark-ui/react'
export const Basic = () => {
const items = ['React', 'Solid', 'Vue']
return (
<Select.Root items={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>
{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>
)
}
import { Index, Portal } from 'solid-js/web'
import { Select } from '@ark-ui/solid'
export const Basic = () => {
const items = ['React', 'Solid', 'Vue']
return (
<Select.Root items={items}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>â–¼</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item()}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
import { ChevronDownIcon } from 'lucide-vue-next'
const items = ref(['React', 'Solid', 'Vue'])
</script>
<template>
<Select.Root :items="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>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in items" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
For advanced customizations and item properties like disabled:
import { ChevronDownIcon } from 'lucide-react'
import { Portal, Select } from '@ark-ui/react'
export const Advanced = () => {
type Item = { label: string; value: string; disabled?: boolean }
const items: Item[] = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={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>
{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>
)
}
import { Index, Portal } from 'solid-js/web'
import { Select } from '@ark-ui/solid'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Advanced = () => {
const items: Item[] = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={items}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
import { ChevronDownIcon } from 'lucide-vue-next'
const items = ref([
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
])
</script>
<template>
<Select.Root :items="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>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
To enable multiple item selection:
import { ChevronDownIcon } from 'lucide-react'
import { Portal, Select } from '@ark-ui/react'
export const Multiple = () => {
type Item = { label: string; value: string; disabled?: boolean }
const items: Item[] = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={items} 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>
{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>
)
}
import { Index, Portal } from 'solid-js/web'
import { Select } from '@ark-ui/solid'
export const Multiple = () => {
const items = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={items} multiple>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Index each={items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
const items = ref([
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
])
</script>
<template>
<Select.Root :items="items" 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>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
For scenarios where you want to control the Select component’s state:
import { ChevronDownIcon } from 'lucide-react'
import { useState } from 'react'
import { Portal, Select } from '@ark-ui/react'
export const Controlled = () => {
type Item = { label: string; value: string; disabled?: boolean }
const [_, setSelectedItems] = useState<Item[]>([])
const items: Item[] = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={items} 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>
{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>
)
}
import { createSignal } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
import { Select } from '@ark-ui/solid'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Controlled = () => {
const [, setSelectedItems] = createSignal<Item[]>([])
const items: Item[] = [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
]
return (
<Select.Root items={items} onValueChange={(e) => setSelectedItems(e.items)}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
import { ChevronDownIcon } from 'lucide-vue-next'
const items = ref([
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
])
const value = ref(['vue'])
</script>
<template>
<Select.Root :items="items" v-model="value">
<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>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
See how to use the Select component with popular form libraries:
Story not foundStory not foundStory not foundUnder the hood for React and Solid frameworks, we supply a complex prop type with a generic so that the type of the items prop matches the param type in the function signatures for props such as the isItemDisabled prop, say. (See the api reference table below) Unfortunately, generic typing is not supported in Vue for components that contain props with slots and/or emits. Therefore, you will not expect updated typing in this way.
If you have a solution or a workaround to this problem, we would love the contribution and request that you open a Github idea discussion to let us know a PoC you have to share!
| Prop | Type | Default |
|---|---|---|
itemsThe options of the select | T[] | readonly T[] | |
asChildRender as a different element type. | boolean | |
closeOnSelectWhether the select should close after an item is selected | boolean | |
defaultOpenThe initial open state of the select when it is first rendered.
Use when you do not need to control its open state. | boolean | |
defaultValueThe initial value of the select when it is first rendered.
Use when you do not need to control the state of the select. | string[] | |
disabledWhether the select is disabled | boolean | |
formThe associate form of the underlying select. | string | |
highlightedValueThe key of the highlighted item | string | |
idThe unique identifier of the machine. | string | |
idsThe ids of the elements in the select. Useful for composition. | 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
}> | |
invalidWhether the select is invalid | boolean | |
isItemDisabledWhether the item is disabled | (item: T) => boolean | |
itemToStringThe label of the item | (item: T) => string | |
itemToValueThe value of the item | (item: T) => string | |
lazyMountWhether to enable lazy mounting | boolean | false |
loopFocusWhether to loop the keyboard navigation through the options | boolean | |
multipleWhether to allow multiple selection | boolean | |
nameThe `name` attribute of the underlying select. | string | |
onExitCompleteFunction called when the animation ends in the closed state. | () => void | |
onFocusOutsideFunction called when the focus is moved outside the component | (event: FocusOutsideEvent) => void | |
onHighlightChangeThe callback fired when the highlighted item changes. | (details: HighlightChangeDetails<T>) => void | |
onInteractOutsideFunction called when an interaction happens outside the component | (event: InteractOutsideEvent) => void | |
onOpenChangeFunction called when the popup is opened | (details: OpenChangeDetails) => void | |
onPointerDownOutsideFunction called when the pointer is pressed down outside the component | (event: PointerDownOutsideEvent) => void | |
onValueChangeThe callback fired when the selected item changes. | (details: ValueChangeDetails<T>) => void | |
openWhether the select menu is open | boolean | |
positioningThe positioning options of the menu. | PositioningOptions | |
presentWhether the node is present (controlled by the user) | boolean | |
readOnlyWhether the select is read-only | boolean | |
scrollToIndexFnFunction to scroll to a specific index | (details: ScrollToIndexDetails) => void | |
unmountOnExitWhether to unmount on exit. | boolean | false |
valueThe keys of the selected items | string[] |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean | |
item | any |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean |
| Prop | Type | Default |
|---|---|---|
asChildRender as a different element type. | boolean | |
placeholderText to display when no value is selected. | string |
Previous
Segment GroupNext
Slider