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-part
attribute 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 { Portal, Select } from '@ark-ui/react'
import { ChevronDownIcon } from 'lucide-react'
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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">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.Root>
)
}
import { Select } from '@ark-ui/solid'
import { Index, Portal } from 'solid-js/web'
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 id="framework">
<Select.ItemGroupLabel for="framework">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.Root>
)
}
<script setup lang="ts">
import { ChevronDownIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
const basicItems = ref(['React', 'Solid', 'Vue'])
const advancedItems = 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="basicItems">
<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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in basicItems" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
</Select.Root>
</template>
For advanced customizations and item properties like disabled
:
import { Portal, Select } from '@ark-ui/react'
import { ChevronDownIcon } from 'lucide-react'
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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">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.Root>
)
}
import { Select } from '@ark-ui/solid'
import { Index, Portal } from 'solid-js/web'
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 id="framework">
<Select.ItemGroupLabel for="framework">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.Root>
)
}
<script setup lang="ts">
import { ChevronDownIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
const basicItems = ref(['React', 'Solid', 'Vue'])
const advancedItems = 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="advancedItems">
<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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in advancedItems" :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.Root>
</template>
To enable multiple
item selection:
import { Portal, Select } from '@ark-ui/react'
import { ChevronDownIcon } from 'lucide-react'
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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">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.Root>
)
}
import { Select } from '@ark-ui/solid'
import { Index, Portal } from 'solid-js/web'
const Multiple = () => {
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.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup id="framework">
<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.Root>
)
}
<script setup lang="ts">
import { ChevronDownIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
const basicItems = ref(['React', 'Solid', 'Vue'])
const advancedItems = 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="advancedItems" 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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in advancedItems" :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.Root>
</template>
For scenarios where you want to control the Select component’s state:
import { Portal, Select } from '@ark-ui/react'
import { ChevronDownIcon } from 'lucide-react'
import { useState } from 'react'
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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">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.Root>
)
}
import { Select } from '@ark-ui/solid'
import { createSignal } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
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 id="framework">
<Select.ItemGroupLabel for="framework">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.Root>
)
}
<script setup lang="ts">
import { ChevronDownIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { Select } from '@ark-ui/vue'
const basicItems = ref(['React', 'Solid', 'Vue'])
const advancedItems = 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="advancedItems" 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 id="framework">
<Select.ItemGroupLabel htmlFor="framework">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in advancedItems" :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.Root>
</template>
See how to use the Select component with popular form libraries:
import { Select } from '@ark-ui/react'
import { ChevronsDownUpIcon } from 'lucide-react'
import { Controller, useForm, type SubmitHandler } from 'react-hook-form'
const FormLibrary = () => {
type Inputs = {
framework: string
}
const items = ['React', 'Solid', 'Vue']
const { control, handleSubmit } = useForm<Inputs>()
const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="framework"
control={control}
render={({ field }) => (
<Select.Root onValueChange={(e) => field.onChange(e?.value)} items={items}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronsDownUpIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup id="framework">
<Select.ItemGroupLabel htmlFor="framework">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>
</Select.Root>
)}
/>
<button type="submit">Submit</button>
</form>
)
}
Story not available
Story not available
Prop | Type | Default |
---|---|---|
items The options of the select | T[] | readonly T[] | |
asChild Render as a different element type. | boolean | |
closeOnSelect Whether the select should close after an item is selected | boolean | |
defaultValue The initial value of the select. | string[] | |
dir The document's text/writing direction. | 'ltr' | 'rtl' | "ltr" |
disabled Whether the select is disabled | boolean | |
form The associate form of the underlying select. | string | |
getRootNode A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. | () => Node | ShadowRoot | Document | |
highlightedValue The key of the highlighted item | string | |
id The unique identifier of the machine. | string | |
ids The 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
}> | |
invalid Whether the select is invalid | boolean | |
isItemDisabled Whether the item is disabled | (item: T) => boolean | |
itemToString The label of the item | (item: T) => string | |
itemToValue The value of the item | (item: T) => string | |
lazyMount Whether to enable lazy mounting | boolean | false |
loop Whether to loop the keyboard navigation through the options | boolean | |
multiple Whether to allow multiple selection | boolean | |
name The `name` attribute of the underlying select. | string | |
onExitComplete Function called when the animation ends in the closed state. | () => void | |
onFocusOutside Function called when the focus is moved outside the component | (event: FocusOutsideEvent) => void | |
onHighlightChange The callback fired when the highlighted item changes. | (details: HighlightChangeDetails<T>) => void | |
onInteractOutside Function called when an interaction happens outside the component | (event: InteractOutsideEvent) => void | |
onOpenChange Function called when the popup is opened | (details: OpenChangeDetails) => void | |
onPointerDownOutside Function called when the pointer is pressed down outside the component | (event: PointerDownOutsideEvent) => void | |
onValueChange The callback fired when the selected item changes. | (details: ValueChangeDetails<T>) => void | |
open Whether the select menu is open | boolean | |
positioning The positioning options of the menu. | PositioningOptions | |
present Whether the node is present (controlled by the user) | boolean | |
readOnly Whether the select is read-only | boolean | |
selectOnBlur Whether to select the highlighted item when the user presses Tab, and the menu is open. | boolean | |
unmountOnExit Whether to unmount on exit. | boolean | false |
value The keys of the selected items | string[] |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean | |
item | any |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
id | string | |
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean | |
placeholder Text to display when no value is selected. | string |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
htmlFor | string | |
asChild Render as a different element type. | boolean |
Previous
Segment GroupNext
Slider