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-partattribute 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 { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const Basic = () => {
return (
<Select.Root className={styles.Root} collection={frameworks}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select" />
</Select.Trigger>
<div className={styles.Indicators}>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{frameworks.items.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const Basic = () => {
return (
<Select.Root class={styles.Root} collection={frameworks}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={frameworks.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
</script>
<template>
<Select.Root :class="styles.Root" :collection="frameworks">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select" />
</Select.Trigger>
<div :class="styles.Indicators">
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in frameworks.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
</script>
<Select.Root class={styles.Root} collection={frameworks}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each frameworks.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
Controlled Value
Use the value and onValueChange props to control the selected items.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/select.module.css'
interface Item {
label: string
value: string
disabled?: boolean | undefined
}
export const Controlled = () => {
const [value, setValue] = useState<string[]>([])
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
const handleValueChange = (details: Select.ValueChangeDetails<Item>) => {
setValue(details.value)
}
return (
<Select.Root className={styles.Root} collection={collection} value={value} onValueChange={handleValueChange}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div className={styles.Indicators}>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Controlled = () => {
const [value, setValue] = createSignal<string[]>([])
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
const handleValueChange = (details: Select.ValueChangeDetails<Item>) => {
setValue(details.value)
}
return (
<Select.Root class={styles.Root} collection={collection} value={value()} onValueChange={handleValueChange}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import styles from 'styles/select.module.css'
interface Item {
label: string
value: string
disabled?: boolean
}
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
const value = ref<string[]>(['vue'])
</script>
<template>
<Select.Root :class="styles.Root" :collection="collection" v-model="value">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
</Select.Trigger>
<div :class="styles.Indicators">
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
interface Item {
label: string
value: string
disabled?: boolean
}
let value = $state<string[]>([])
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
</script>
<Select.Root class={styles.Root} {collection} bind:value>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each collection.items as item (item.value)}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
Grouping
Grouping related options can be useful for organizing options into categories.
- Use the
groupByprop to configure the grouping of the items. - Use the
collection.group()method to get the grouped items. - Use the
Select.ItemGroupandSelect.ItemGroupLabelcomponents to render the grouped items.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
export const Grouping = () => {
return (
<Select.Root className={styles.Root} collection={frameworks}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div className={styles.Indicators}>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
{frameworks.group().map(([type, group]) => (
<Select.ItemGroup className={styles.ItemGroup} key={type}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>{type}</Select.ItemGroupLabel>
{group.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
))}
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { For, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
export const Grouping = () => {
return (
<Select.Root class={styles.Root} collection={frameworks}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<For each={frameworks.group()}>
{([type, group]) => (
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>{type}</Select.ItemGroupLabel>
<For each={group}>
{(item) => (
<Select.Item class={styles.Item} item={item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</For>
</Select.ItemGroup>
)}
</For>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
</script>
<template>
<Select.Root :class="styles.Root" :collection="collection">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
</Select.Trigger>
<div :class="styles.Indicators">
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup v-for="[type, group] in collection.group()" :key="type" :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">{{ type }}</Select.ItemGroupLabel>
<Select.Item v-for="item in group" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
</script>
<Select.Root class={styles.Root} collection={frameworks}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
{#each frameworks.group() as [type, group]}
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>{type}</Select.ItemGroupLabel>
{#each group as item (item.value)}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
{/each}
</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 { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
export const Multiple = () => {
return (
<Select.Root className={styles.Root} collection={frameworks} multiple>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{frameworks.items.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
export const Multiple = () => {
return (
<Select.Root class={styles.Root} collection={frameworks} multiple>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={frameworks.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
</script>
<template>
<Select.Root :class="styles.Root" :collection="frameworks" multiple>
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select" />
</Select.Trigger>
<div :class="styles.Indicators">
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in frameworks.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
</script>
<Select.Root class={styles.Root} collection={frameworks} multiple>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each frameworks.items as item (item.value)}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
Form Library
Here's an example of integrating the Select component with a form library.
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon } from 'lucide-react'
import { Controller, type SubmitHandler, useForm } from 'react-hook-form'
import styles from 'styles/select.module.css'
import button from 'styles/button.module.css'
interface Inputs {
framework: string
}
export const FormLibrary = () => {
const { control, handleSubmit } = useForm<Inputs>({
defaultValues: { framework: 'React' },
})
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const onSubmit: SubmitHandler<Inputs> = (data) => {
window.alert(JSON.stringify(data))
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="framework"
control={control}
render={({ field }) => (
<Select.Root
className={styles.Root}
collection={collection}
value={field.value ? [field.value] : []}
onValueChange={(e) => field.onChange(e.value[0])}
name={field.name}
onInteractOutside={() => field.onBlur()}
>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.HiddenSelect />
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger className={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item} item={item}>
<Select.ItemText className={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Select.Root>
)}
/>
<button className={button.Root} style={{ marginTop: '1rem' }} type="submit">
Submit
</button>
</form>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { createForm, getValue, setValue } from '@modular-forms/solid'
import { createMemo } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
],
})
export const FormLibrary = () => {
const [formStore, { Form, Field }] = createForm({
initialValues: { value: 'solid' },
})
const value = createMemo(() => getValue(formStore, 'value'))
return (
<>
<div style={{ 'margin-bottom': '1rem' }}>Value is {value()}</div>
<Form
onSubmit={(data) => {
window.alert(JSON.stringify(data))
}}
>
<Field name="value">
{(field, props) => (
<Select.Root
class={styles.Root}
collection={frameworks}
value={field.value ? [field.value] : []}
invalid={!!field.error}
name={field.name}
onValueChange={(e) => setValue(formStore, field.name, e.value[0])}
>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={frameworks.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect {...props} />
</Select.Root>
)}
</Field>
<button class={button.Root} style={{ 'margin-top': '1rem' }} type="submit">
Submit
</button>
</Form>
</>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { useForm, useField } from 'vee-validate'
import { ChevronsUpDownIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
import button from 'styles/button.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
const { handleSubmit, values } = useForm({
initialValues: {
framework: 'vue',
},
})
const { value: framework, setValue } = useField<string>('framework')
const onSubmit = handleSubmit((values) => {
window.alert(JSON.stringify(values))
})
</script>
<template>
<div>
<div style="margin-bottom: 1rem">Value is {{ values.framework }}</div>
<form @submit="onSubmit">
<Select.Root
:class="styles.Root"
:collection="frameworks"
:model-value="framework ? [framework] : []"
@value-change="(e) => setValue(e.value[0])"
>
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in frameworks.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect name="framework" />
</Select.Root>
<button :class="button.Root" style="margin-top: 1rem" type="submit">Submit</button>
</form>
</div>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon } from 'lucide-svelte'
import { createForm } from '@tanstack/svelte-form'
import styles from 'styles/select.module.css'
import button from 'styles/button.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
],
})
const form = createForm(() => ({
defaultValues: {
framework: 'solid',
},
onSubmit: async ({ value }) => {
console.log(value)
},
}))
const formData = $derived(form.state.values)
</script>
<div style="margin-bottom: 1rem;">Value is {formData.framework}</div>
<form
onsubmit={(e) => {
e.preventDefault()
form.handleSubmit()
}}
>
<form.Field name="framework">
{#snippet children(field)}
{@const state = field.state}
<Select.Root
class={styles.Root}
collection={frameworks}
value={state.value ? [state.value] : []}
invalid={state.meta.errors.length > 0}
name={field.name}
onValueChange={(details) => {
field.handleChange(details.value[0])
}}
>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each frameworks.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
{/snippet}
</form.Field>
<button class={button.Root} style="margin-top: 1rem;" type="submit">Submit</button>
</form>
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 { ChevronsUpDownIcon } from 'lucide-react'
import field from 'styles/field.module.css'
import styles from 'styles/select.module.css'
export const WithField = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Field.Root className={field.Root}>
<Select.Root collection={collection} className={styles.Root}>
<Select.Label className={styles.Label}>Label</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content className={styles.Content}>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item} item={item}>
<Select.ItemText className={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText className={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText className={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
)
}
import { Field } from '@ark-ui/solid/field'
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index } from 'solid-js/web'
import field from 'styles/field.module.css'
import styles from 'styles/select.module.css'
export const WithField = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Field.Root class={field.Root}>
<Select.Root collection={collection} class={styles.Root}>
<Select.Label class={styles.Label}>Label</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Index each={collection.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item()}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
)
}
<script setup lang="ts">
import { Field } from '@ark-ui/vue/field'
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
import field from 'styles/field.module.css'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
</script>
<template>
<Field.Root :class="field.Root">
<Select.Root :collection="collection" :class="styles.Root">
<Select.Label :class="styles.Label">Label</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.Item v-for="item in collection.items" :key="item" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText :class="field.HelperText">Additional Info</Field.HelperText>
<Field.ErrorText :class="field.ErrorText">Error Info</Field.ErrorText>
</Field.Root>
</template>
<script lang="ts">
import { Field } from '@ark-ui/svelte/field'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon } from 'lucide-svelte'
import field from 'styles/field.module.css'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
</script>
<Field.Root class={field.Root}>
<Select.Root {collection} class={styles.Root}>
<Select.Label class={styles.Label}>Label</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content class={styles.Content}>
{#each collection.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText class={field.HelperText}>Additional Info</Field.HelperText>
<Field.ErrorText class={field.ErrorText}>Error Info</Field.ErrorText>
</Field.Root>
Async Loading
Here's an example of how to load the items asynchronously when the select is opened.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/select.module.css'
function loadData() {
return new Promise<string[]>((resolve) => {
setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500)
})
}
export const Async = () => {
const [items, setItems] = useState<string[] | null>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const collection = createListCollection<string>({
items: items || [],
})
const handleOpenChange = (details: Select.OpenChangeDetails) => {
if (details.open && items == null) {
setLoading(true)
setError(null)
loadData()
.then((data) => setItems(data))
.catch((err) => setError(err))
.finally(() => setLoading(false))
}
}
return (
<Select.Root className={styles.Root} collection={collection} onOpenChange={handleOpenChange}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
{loading ? (
<div className={styles.Item}>Loading...</div>
) : error ? (
<div className={styles.Item}>Error: {error.message}</div>
) : (
collection.items.map((item) => (
<Select.Item className={styles.Item} key={item} item={item}>
<Select.ItemText className={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))
)}
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Match, Switch, createMemo, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
function loadData() {
return new Promise<string[]>((resolve) => {
setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500)
})
}
export const Async = () => {
const [items, setItems] = createSignal<string[] | null>(null)
const [loading, setLoading] = createSignal(false)
const [error, setError] = createSignal<Error | null>(null)
const collection = createMemo(() =>
createListCollection<string>({
items: items() || [],
}),
)
const handleOpenChange = (details: Select.OpenChangeDetails) => {
if (details.open && items() === null) {
setLoading(true)
setError(null)
loadData()
.then((data) => setItems(data))
.catch((err) => setError(err))
.finally(() => setLoading(false))
}
}
return (
<Select.Root class={styles.Root} collection={collection()} onOpenChange={handleOpenChange}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Switch>
<Match when={loading()}>
<div class={styles.Item}>Loading...</div>
</Match>
<Match when={error()}>
<div class={styles.Item}>Error: {error()?.message}</div>
</Match>
<Match when={items() !== null}>
<Index each={collection().items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item()}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Match>
</Switch>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
// biome-ignore lint/style/useImportType: intentional
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon } from 'lucide-vue-next'
import { computed, ref } from 'vue'
import styles from 'styles/select.module.css'
function loadData() {
return new Promise<string[]>((resolve) => {
setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500)
})
}
const data = ref<string[] | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const collection = computed(() =>
createListCollection({
items: data.value ?? [],
}),
)
const handleOpenChange = async (details: Select.OpenChangeDetails) => {
if (details.open && data.value === null) {
loading.value = true
error.value = null
try {
const result = await loadData()
data.value = result
} catch (err) {
error.value = err as Error
} finally {
loading.value = false
}
}
}
</script>
<template>
<Select.Root :class="styles.Root" :collection="collection" @open-change="handleOpenChange">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<div v-if="loading" :class="styles.Item">Loading...</div>
<div v-else-if="error" :class="styles.Item">Error: {{ error.message }}</div>
<template v-else>
<Select.Item v-for="item in collection.items" :key="item" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</template>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
// biome-ignore lint/style/useImportType: intentional
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
function loadData() {
return new Promise<string[]>((resolve) => {
setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500)
})
}
let data = $state<string[] | null>(null)
let loading = $state(false)
let error = $state<Error | null>(null)
const collection = $derived(
createListCollection({
items: data ?? [],
}),
)
const handleOpenChange = (details: Select.OpenChangeDetails) => {
if (details.open && data === null) {
loading = true
error = null
loadData()
.then((result) => {
data = result
})
.catch((err) => {
error = err
})
.finally(() => {
loading = false
})
}
}
</script>
<Select.Root class={styles.Root} {collection} onOpenChange={handleOpenChange}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
{#if loading}
<div class={styles.Item}>Loading...</div>
{:else if error}
<div class={styles.Item}>Error: {error.message}</div>
{:else}
{#each collection.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
{/if}
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
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 { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const RootProvider = () => {
const select = useSelect({ collection: frameworks })
return (
<>
<button className={button.Root} style={{ marginBottom: '1rem' }} onClick={() => select.focus()}>
Focus
</button>
<Select.RootProvider className={styles.Root} value={select}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div className={styles.Indicators}>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{frameworks.items.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
</>
)
}
import { Select, createListCollection, useSelect } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
export const RootProvider = () => {
const select = useSelect({ collection: frameworks })
return (
<>
<button class={button.Root} style={{ 'margin-bottom': '1rem' }} onClick={() => select().focus()}>
Focus
</button>
<Select.RootProvider class={styles.Root} value={select}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={frameworks.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
</>
)
}
<script setup lang="ts">
import { Select, createListCollection, useSelect } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
import button from 'styles/button.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
const select = useSelect({ collection: frameworks })
</script>
<template>
<button :class="button.Root" style="margin-bottom: 1rem" @click="select.focus()">Focus</button>
<Select.RootProvider :class="styles.Root" :value="select">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
</Select.Trigger>
<div :class="styles.Indicators">
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in frameworks.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.RootProvider>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection, useSelect } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import button from 'styles/button.module.css'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
const id = $props.id()
const select = useSelect({ collection: frameworks, id })
</script>
<div>
<button class={button.Root} style="margin-bottom: 1rem;" onclick={() => select().focus()}>Focus</button>
<Select.RootProvider class={styles.Root} value={select}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
</Select.Trigger>
<div class={styles.Indicators}>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</div>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each frameworks.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
</div>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
Select on Highlight
Here's an example of automatically selecting items when they are highlighted (hovered or navigated to with keyboard).
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection, useSelect } from '@ark-ui/react/select'
import { ChevronsUpDownIcon } from 'lucide-react'
import styles from 'styles/select.module.css'
export const SelectOnHighlight = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const select = useSelect({
collection,
onHighlightChange({ highlightedValue }) {
if (highlightedValue) {
select.selectValue(highlightedValue)
}
},
})
return (
<Select.RootProvider className={styles.Root} value={select}>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger className={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item} item={item}>
<Select.ItemText className={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
)
}
import { Select, createListCollection, useSelect } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
export const SelectOnHighlight = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const select = useSelect({
collection,
onHighlightChange({ highlightedValue }) {
if (highlightedValue) {
select().selectValue(highlightedValue)
}
},
})
return (
<Select.RootProvider class={styles.Root} value={select}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item()}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
)
}
<script setup lang="ts">
import { Select, createListCollection, useSelect } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const select = useSelect({
collection,
onHighlightChange({ highlightedValue }) {
if (highlightedValue) {
select.value.selectValue(highlightedValue)
}
},
})
</script>
<template>
<Select.RootProvider :class="styles.Root" :value="select">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.Item v-for="item in collection.items" :key="item" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.RootProvider>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection, useSelect } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
const select = useSelect({
collection: frameworks,
onHighlightChange({ highlightedValue }) {
if (highlightedValue) {
select().selectValue(highlightedValue)
}
},
})
</script>
<Select.RootProvider class={styles.Root} value={select}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each frameworks.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
Maximum Selected Items
Here's an example of limiting the number of items that can be selected in a multiple select.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/select.module.css'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const MAX_SELECTION = 2
const hasReachedMax = (value: string[]) => value.length >= MAX_SELECTION
export const MaxSelected = () => {
const [value, setValue] = useState<string[]>([])
const collection = createListCollection({
items: items.map((item) => ({
label: item,
value: item,
disabled: hasReachedMax(value) && !value.includes(item),
})),
})
const handleValueChange = (details: Select.ValueChangeDetails) => {
if (hasReachedMax(value) && details.value.length > value.length) return
setValue(details.value)
}
return (
<Select.Root
className={styles.Root}
collection={collection}
multiple
value={value}
onValueChange={handleValueChange}
>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger className={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item.value} item={item}>
<Select.ItemText className={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-solid'
import { Index, createMemo, createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const MAX_SELECTION = 2
const hasReachedMax = (value: string[]) => value.length >= MAX_SELECTION
export const MaxSelected = () => {
const [value, setValue] = createSignal<string[]>([])
const collection = createMemo(() =>
createListCollection({
items: items.map((item) => ({
label: item,
value: item,
disabled: hasReachedMax(value()) && !value().includes(item),
})),
}),
)
const handleValueChange = (details: Select.ValueChangeDetails) => {
if (hasReachedMax(value()) && details.value.length > value().length) return
setValue(details.value)
}
return (
<Select.Root
class={styles.Root}
collection={collection()}
multiple
value={value()}
onValueChange={handleValueChange}
>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={collection().items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item().label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
// biome-ignore lint/style/useImportType: intentional
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-vue-next'
import { computed, ref } from 'vue'
import styles from 'styles/select.module.css'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const value = ref<string[]>([])
const MAX_SELECTION = 2
const hasReachedMax = (value: string[]) => value.length >= MAX_SELECTION
const collection = computed(() =>
createListCollection({
items: items.map((item) => ({
label: item,
value: item,
disabled: hasReachedMax(value.value) && !value.value.includes(item),
})),
}),
)
const handleValueChange = (details: Select.ValueChangeDetails) => {
if (hasReachedMax(value.value) && details.value.length > value.value.length) return
value.value = details.value
}
</script>
<template>
<Select.Root :class="styles.Root" :collection="collection" multiple :value="value" @value-change="handleValueChange">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger :class="styles.ClearTrigger">
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item.value" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item.label }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
// biome-ignore lint/style/useImportType: intentional
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon, XIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const items = ['React', 'Solid', 'Vue', 'Svelte']
const MAX_SELECTION = 2
const hasReachedMax = (value: string[]) => value.length >= MAX_SELECTION
let value = $state<string[]>([])
const collection = $derived(
createListCollection({
items: items.map((item) => ({
label: item,
value: item,
disabled: hasReachedMax(value) && !value.includes(item),
})),
}),
)
const handleValueChange = (details: Select.ValueChangeDetails) => {
if (hasReachedMax(value) && details.value.length > value.length) return
value = details.value
}
</script>
<Select.Root class={styles.Root} {collection} multiple {value} onValueChange={handleValueChange}>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>
<XIcon />
</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each collection.items as item (item.value)}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item.label}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
Lazy Mount
The lazyMount and unmountOnExit props allow you to control when the select content is mounted and unmounted from the
DOM. This can improve performance by only rendering the content when needed.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronsUpDownIcon } from 'lucide-react'
import styles from 'styles/select.module.css'
export const LazyMount = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Alpine'],
})
return (
<Select.Root className={styles.Root} collection={collection} lazyMount unmountOnExit>
<Select.Label className={styles.Label}>Framework</Select.Label>
<Select.Control className={styles.Control}>
<Select.Trigger className={styles.Trigger}>
<Select.ValueText className={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator className={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger className={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content className={styles.Content}>
<Select.ItemGroup className={styles.ItemGroup}>
<Select.ItemGroupLabel className={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item className={styles.Item} key={item} item={item}>
<Select.ItemText className={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator className={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronsUpDownIcon } from 'lucide-solid'
import { Index, Portal } from 'solid-js/web'
import styles from 'styles/select.module.css'
export const LazyMount = () => {
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Alpine'],
})
return (
<Select.Root class={styles.Root} collection={collection} lazyMount unmountOnExit>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item class={styles.Item} item={item()}>
<Select.ItemText class={styles.ItemText}>{item()}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronsUpDownIcon } from 'lucide-vue-next'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Alpine'],
})
</script>
<template>
<Select.Root :class="styles.Root" :collection="collection" :lazy-mount="true" :unmount-on-exit="true">
<Select.Label :class="styles.Label">Framework</Select.Label>
<Select.Control :class="styles.Control">
<Select.Trigger :class="styles.Trigger">
<Select.ValueText :class="styles.ValueText" placeholder="Select a Framework" />
<Select.Indicator :class="styles.Indicator">
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger :class="styles.ClearTrigger">Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content :class="styles.Content">
<Select.ItemGroup :class="styles.ItemGroup">
<Select.ItemGroupLabel :class="styles.ItemGroupLabel">Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item" :item="item" :class="styles.Item">
<Select.ItemText :class="styles.ItemText">{{ item }}</Select.ItemText>
<Select.ItemIndicator :class="styles.ItemIndicator">✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
<script lang="ts">
import { Portal } from '@ark-ui/svelte/portal'
import { Select, createListCollection } from '@ark-ui/svelte/select'
import { ChevronsUpDownIcon } from 'lucide-svelte'
import styles from 'styles/select.module.css'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Alpine'],
})
</script>
<Select.Root class={styles.Root} {collection} lazyMount unmountOnExit>
<Select.Label class={styles.Label}>Framework</Select.Label>
<Select.Control class={styles.Control}>
<Select.Trigger class={styles.Trigger}>
<Select.ValueText class={styles.ValueText} placeholder="Select a Framework" />
<Select.Indicator class={styles.Indicator}>
<ChevronsUpDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger class={styles.ClearTrigger}>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content class={styles.Content}>
<Select.ItemGroup class={styles.ItemGroup}>
<Select.ItemGroupLabel class={styles.ItemGroupLabel}>Frameworks</Select.ItemGroupLabel>
{#each collection.items as item}
<Select.Item class={styles.Item} {item}>
<Select.ItemText class={styles.ItemText}>{item}</Select.ItemText>
<Select.ItemIndicator class={styles.ItemIndicator}>✓</Select.ItemIndicator>
</Select.Item>
{/each}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
Guides
Type-Safety
The Select.RootComponent type enables you to create closed, strongly typed wrapper components that maintain full type
safety for collection items.
This is particularly useful when building reusable select components with custom props and consistent styling.
import { Select as ArkSelect, type CollectionItem } from '@ark-ui/react/select'
import { createListCollection } from '@ark-ui/react/collection'
interface SelectProps<T extends CollectionItem> extends ArkSelect.RootProps<T> {}
const Select: ArkSelect.RootComponent = (props) => {
return <ArkSelect.Root {...props}>{/* ... */}</ArkSelect.Root>
}
Then, you can use the Select component as follows:
const App = () => {
const collection = createListCollection({
initialItems: [
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
return (
<Select
collection={collection}
onValueChange={(e) => {
// this will be strongly typed Array<{ label: string, value: string }>
console.log(e.items)
}}
>
{/* ... */}
</Select>
)
}
Usage in Popover or Dialog
When using the Select component within a Popover or Dialog, avoid rendering its content within a Portal or
Teleport.
This ensures the Select's content stays within the Popover/Dialog's DOM hierarchy rather than being portalled to the document body, maintaining proper interaction and accessibility behavior.
Hidden Select
The Select.HiddenSelect component renders a native HTML <select> element that's visually hidden but remains in the
DOM. This component is essential for:
- Form submission: Native form submission and serialization work seamlessly since the actual
<select>element exists in the DOM - Browser auto-fill: Browsers can properly auto-fill the select based on previously submitted form data
- Progressive enhancement: Forms remain functional even if JavaScript fails to load
<Select.Root>
<Select.HiddenSelect />
{/* Other Select components */}
</Select.Root>
The hidden select automatically syncs with the Select component's value, ensuring form data is always up-to-date.
Empty State
You can create an empty state component that displays when there are no items in the collection. Use the
useSelectContext hook to check the collection size:
const SelectEmpty = (props: React.ComponentProps<'div'>) => {
const select = useSelectContext()
if (select.collection.size === 0) {
return <div {...props} role="presentation" />
}
return null
}
Then use it within your Select content:
<Select.Content>
<SelectEmpty>No items to display</SelectEmpty>
{/* Your items */}
</Select.Content>
Available height and width
The following css variables are exposed to the Select.Positioner which you can use to style the Select.Content
/* width of the select trigger */
--reference-width: <pixel-value>;
/* width of the available viewport */
--available-width: <pixel-value>;
/* height of the available viewport */
--available-height: <pixel-value>;
For example, if you want to make sure the maximum height doesn't exceed the available height, you can use the following:
[data-scope='select'][data-part='content'] {
max-height: calc(var(--available-height) - 100px);
}
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
collection | ListCollection<T>The collection of items | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
closeOnSelect | true | booleanWhether the select should close after an item is selected |
composite | true | booleanWhether the select is a composed with other composite widgets like tabs or combobox |
defaultHighlightedValue | stringThe initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the select. | |
defaultOpen | booleanWhether the select's open state is controlled by the user | |
defaultValue | string[]The initial default value of the select when rendered. Use when you don't need to control the value of the select. | |
deselectable | booleanWhether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection | |
disabled | booleanWhether the select is disabled | |
form | stringThe associate form of the underlying select. | |
highlightedValue | stringThe controlled key of the highlighted item | |
id | stringThe 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 | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
invalid | booleanWhether the select is invalid | |
lazyMount | false | booleanWhether to enable lazy mounting |
loopFocus | false | booleanWhether to loop the keyboard navigation through the options |
multiple | booleanWhether to allow multiple selection | |
name | stringThe `name` attribute of the underlying select. | |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails<T>) => voidThe callback fired when the highlighted item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => voidFunction called when the popup is opened | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component | |
onSelect | (details: SelectionDetails) => voidFunction called when an item is selected | |
onValueChange | (details: ValueChangeDetails<T>) => voidThe callback fired when the selected item changes. | |
open | booleanWhether the select menu is open | |
positioning | PositioningOptionsThe positioning options of the menu. | |
present | booleanWhether the node is present (controlled by the user) | |
readOnly | booleanWhether the select is read-only | |
required | booleanWhether the select is required | |
scrollToIndexFn | (details: ScrollToIndexDetails) => voidFunction to scroll to a specific index | |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
unmountOnExit | false | booleanWhether to unmount on exit. |
value | string[]The controlled keys of the selected items |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | root |
[data-invalid] | Present when invalid |
[data-readonly] | Present when read-only |
ClearTrigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | clear-trigger |
[data-invalid] | Present when invalid |
Content
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| CSS Variable | Description |
|---|---|
--layer-index | The index of the dismissable in the layer stack |
--nested-layer-count | The number of nested selects |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | content |
[data-state] | "open" | "closed" |
[data-nested] | listbox |
[data-has-nested] | listbox |
[data-placement] | The placement of the content |
[data-activedescendant] | The id the active descendant of the content |
Control
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Indicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ItemGroup
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | item-group |
[data-disabled] | Present when disabled |
ItemIndicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | item-indicator |
[data-state] | "checked" | "unchecked" |
Item
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
item | anyThe item to render | |
persistFocus | booleanWhether hovering outside should clear the highlighted state |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | item-text |
[data-state] | "checked" | "unchecked" |
[data-disabled] | Present when disabled |
[data-highlighted] | Present when highlighted |
Label
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | label |
[data-disabled] | Present when disabled |
[data-invalid] | Present when invalid |
[data-readonly] | Present when read-only |
[data-required] | Present when required |
List
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Positioner
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| CSS Variable | Description |
|---|---|
--reference-width | The width of the reference element |
--reference-height | The height of the root |
--available-width | The available width in viewport |
--available-height | The available height in viewport |
--x | The x position for transform |
--y | The y position for transform |
--z-index | The z-index value |
--transform-origin | The transform origin for animations |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseSelectReturn<T> | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | booleanWhether to enable lazy mounting |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
present | booleanWhether the node is present (controlled by the user) | |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
unmountOnExit | false | booleanWhether to unmount on exit. |
Trigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[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
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
placeholder | stringText to display when no value is selected. |
| Data Attribute | Value |
|---|---|
[data-scope] | select |
[data-part] | value-text |
[data-disabled] | Present when disabled |
[data-invalid] | Present when invalid |
[data-focus] | Present when focused |
Context
These are the properties available when using Select.Context, useSelectContext hook or useSelect hook.
API
| Property | Type |
|---|---|
focused | booleanWhether the select is focused |
open | booleanWhether the select is open |
empty | booleanWhether the select value is empty |
highlightedValue | stringThe value of the highlighted item |
highlightedItem | VThe highlighted item |
setHighlightValue | (value: string) => voidFunction to highlight a value |
clearHighlightValue | VoidFunctionFunction to clear the highlighted value |
selectedItems | V[]The selected items |
hasSelectedItems | booleanWhether there's a selected option |
value | string[]The selected item keys |
valueAsString | stringThe string representation of the selected items |
selectValue | (value: string) => voidFunction to select a value |
selectAll | VoidFunctionFunction to select all values |
setValue | (value: string[]) => voidFunction to set the value of the select |
clearValue | (value?: string) => voidFunction to clear the value of the select. If a value is provided, it will only clear that value, otherwise, it will clear all values. |
focus | VoidFunctionFunction to focus on the select input |
getItemState | (props: ItemProps<any>) => ItemStateReturns the state of a select item |
setOpen | (open: boolean) => voidFunction to open or close the select |
collection | ListCollection<V>Function to toggle the select |
reposition | (options?: Partial<PositioningOptions>) => voidFunction to set the positioning options of the select |
multiple | booleanWhether the select allows multiple selections |
disabled | booleanWhether the select is disabled |
Accessibility
Complies with the Listbox WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
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. |