Select
Displays a list of options for the user to pick from.
You can explore the select component in the following curated examples.
Async Select
Render a Select that fetches items asynchronously.
Select Tabs
Render a Tabs component within a Select.
Virtualized Select
Render a Select that virtualizes its options.
Anatomy
To set up the select correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Examples
Learn how to use the Select
component in your project. Let's take a look at the most basic
example:
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
export const Basic = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
return (
<Select.Root collection={collection}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item} item={item}>
<Select.ItemText>{item}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { Index, Portal } from 'solid-js/web'
export const Basic = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
return (
<Select.Root collection={collection}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>▼</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item()}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue'],
})
</script>
<template>
<Select.Root :collection="collection">
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
Advanced Customization
For advanced customizations and item properties like disabled
:
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
export const Advanced = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item.value} item={item}>
<Select.ItemText>{item.label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { Index, Portal } from 'solid-js/web'
export const Advanced = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
</script>
<template>
<Select.Root :collection="collection">
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
Multiple Selection
To enable multiple
item selection:
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
export const Multiple = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection} multiple>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item.value} item={item}>
<Select.ItemText>{item.label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { Index, Portal } from 'solid-js/web'
export const Multiple = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection} multiple>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
</script>
<template>
<Select.Root :collection="collection" multiple>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
Controlled Component
For scenarios where you want to control the Select component's state:
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
import { useState } from 'react'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Controlled = () => {
const [_, setSelectedItems] = useState<Item[]>([])
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection} onValueChange={(e) => setSelectedItems(e.items)}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item.value} item={item}>
<Select.ItemText>{item.label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
import { Select, createListCollection } from '@ark-ui/solid/select'
import { createSignal } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
interface Item {
label: string
value: string
disabled?: boolean
}
export const Controlled = () => {
const [, setSelectedItems] = createSignal<Item[]>([])
const collection = createListCollection<Item>({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
return (
<Select.Root collection={collection} onValueChange={(e) => setSelectedItems(e.items)}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.Root>
)
}
<script setup lang="ts">
import { Select, createListCollection } from '@ark-ui/vue/select'
import { ref } from 'vue'
import { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte', disabled: true },
],
})
const value = ref(['vue'])
</script>
<template>
<Select.Root :collection="collection" v-model="value">
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item.value" :item="item">
<Select.ItemText>{{ item.label }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
</template>
Usage with a Form Library
See how to use the Select component with popular form libraries:
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
import { type SubmitHandler, useForm } from 'react-hook-form'
interface Inputs {
framework: string
}
export const FormLibrary = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
const { register, handleSubmit } = useForm<Inputs>()
const onSubmit: SubmitHandler<Inputs> = (data) => window.alert(JSON.stringify(data))
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Select.Root collection={collection}>
<Select.Label>Framework</Select.Label>
<Select.HiddenSelect {...register('framework')} />
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item} item={item}>
<Select.ItemText>{item}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Select.Root>
<button type="submit">Submit</button>
</form>
)
}
import { Select, createListCollection } from '@ark-ui/solid'
import { createForm, getValue, setValue } from '@modular-forms/solid'
import { createMemo } from 'solid-js'
import { Index, Portal } from 'solid-js/web'
export const WithFormLibrary = () => {
const frameworks = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Vue', value: 'vue' },
],
})
const [formStore, { Form, Field }] = createForm({
initialValues: { value: 'solid' },
})
const value = createMemo(() => getValue(formStore, 'value'))
return (
<>
<div>Value is {value()}</div>
<Form
onSubmit={(e) => {
console.log(e.value)
}}
>
<Field name="value">
{(field, props) => (
<Select.Root
collection={frameworks}
value={field.value ? [field.value] : undefined}
invalid={!!field.error}
name={field.name}
onValueChange={(e) => {
setValue(formStore, field.name, e.value[0])
}}
>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={frameworks.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item().label}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect {...props} />
</Select.Root>
)}
</Field>
<button type="submit">Submit</button>
</Form>
</>
)
}
Example not found
Using the Field Component
The Field
component helps manage form-related state and accessibility attributes of a select.
It includes handling ARIA labels, helper text, and error text to ensure proper accessibility.
import { Field } from '@ark-ui/react/field'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
export const WithField = (props: Field.RootProps) => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
return (
<Field.Root {...props}>
<Select.Root collection={collection}>
<Select.Label>Label</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content>
{collection.items.map((item) => (
<Select.Item key={item} item={item}>
<Select.ItemText>{item}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
)
}
import { Field } from '@ark-ui/solid/field'
import { Select, createListCollection } from '@ark-ui/solid/select'
import { ChevronDownIcon } from 'lucide-solid'
import { Index } from 'solid-js/web'
export const WithField = (props: Field.RootProps) => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
return (
<Field.Root {...props}>
<Select.Root collection={collection}>
<Select.Label>Label</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
</Select.Control>
<Select.Positioner>
̦
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item()}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<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 { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue'],
})
</script>
<template>
<Field.Root>
<Select.Root :collection="collection">
<Select.Label>Label</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.Root>
<Field.HelperText>Additional Info</Field.HelperText>
<Field.ErrorText>Error Info</Field.ErrorText>
</Field.Root>
</template>
Using the Root Provider
The RootProvider
component provides a context for the select. It accepts the value of the useSelect
hook.
You can leverage it to access the component state and methods from outside the select.
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection, useSelect } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'
export const RootProvider = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
const select = useSelect({ collection: collection })
return (
<>
<button onClick={() => select.focus()}>Focus</button>
<Select.RootProvider value={select}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
{collection.items.map((item) => (
<Select.Item key={item} item={item}>
<Select.ItemText>{item}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
))}
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
</>
)
}
import { Select, createListCollection, useSelect } from '@ark-ui/solid/select'
import { Index, Portal } from 'solid-js/web'
export const RootProvider = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })
const select = useSelect({ collection: collection })
return (
<>
<button onClick={() => select().focus()}>Focus</button>
<Select.RootProvider value={select}>
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>▼</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Index each={collection.items}>
{(item) => (
<Select.Item item={item()}>
<Select.ItemText>{item()}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
)}
</Index>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Portal>
<Select.HiddenSelect />
</Select.RootProvider>
</>
)
}
<script setup lang="ts">
import { Select, createListCollection, useSelect } from '@ark-ui/vue/select'
import { ChevronDownIcon } from 'lucide-vue-next'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue'],
})
const select = useSelect({ collection: collection })
</script>
<template>
<button @click="select.focus()">Focus</button>
<Select.RootProvider :value="select">
<Select.Label>Framework</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="Select a Framework" />
<Select.Indicator>
<ChevronDownIcon />
</Select.Indicator>
</Select.Trigger>
<Select.ClearTrigger>Clear</Select.ClearTrigger>
</Select.Control>
<Teleport to="body">
<Select.Positioner>
<Select.Content>
<Select.ItemGroup>
<Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
<Select.Item v-for="item in collection.items" :key="item" :item="item">
<Select.ItemText>{{ item }}</Select.ItemText>
<Select.ItemIndicator>✓</Select.ItemIndicator>
</Select.Item>
</Select.ItemGroup>
</Select.Content>
</Select.Positioner>
</Teleport>
<Select.HiddenSelect />
</Select.RootProvider>
</template>
If you're using the
RootProvider
component, you don't need to use theRoot
component.
API Reference
Root
Prop | Default | Type |
---|---|---|
collection | ListCollection<T> The collection of items | |
asChild | (props: ParentProps<'div'>) => Element Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
closeOnSelect | true | boolean Whether the select should close after an item is selected |
composite | true | boolean Whether the select is a composed with other composite widgets like tabs or combobox |
defaultOpen | boolean The initial open state of the select when it is first rendered. Use when you do not need to control its open state. | |
defaultValue | string[] The initial value of the select when it is first rendered. Use when you do not need to control the state of the select. | |
deselectable | boolean Whether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection | |
disabled | boolean Whether the select is disabled | |
form | string The associate form of the underlying select. | |
highlightedValue | string The key of the highlighted item | |
ids | Partial<{
root: string
content: string
control: string
trigger: string
clearTrigger: string
label: string
hiddenSelect: string
positioner: string
item(id: string | number): string
itemGroup(id: string | number): string
itemGroupLabel(id: string | number): string
}> The ids of the elements in the select. Useful for composition. | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
invalid | boolean Whether the select is invalid | |
lazyMount | false | boolean Whether to enable lazy mounting |
loopFocus | false | boolean Whether to loop the keyboard navigation through the options |
multiple | boolean Whether to allow multiple selection | |
name | string The `name` attribute of the underlying select. | |
onExitComplete | () => void Function called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => void Function called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails<T>) => void The callback fired when the highlighted item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => void Function called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => void Function called when the popup is opened | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void Function called when the pointer is pressed down outside the component | |
onValueChange | (details: ValueChangeDetails<T>) => void The callback fired when the selected item changes. | |
open | boolean Whether the select menu is open | |
positioning | PositioningOptions The positioning options of the menu. | |
present | boolean Whether the node is present (controlled by the user) | |
readOnly | boolean Whether the select is read-only | |
required | boolean Whether the select is required | |
scrollToIndexFn | (details: ScrollToIndexDetails) => void Function to scroll to a specific index | |
unmountOnExit | false | boolean Whether to unmount on exit. |
value | string[] The 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 | (props: ParentProps<'button'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use 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] | content |
[data-state] | "open" | "closed" |
[data-placement] | The placement of the content |
[data-activedescendant] |
Control
Prop | Default | Type |
---|---|---|
asChild | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'select'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
item | any The item to render | |
persistFocus | boolean Whether hovering outside should clear the highlighted state |
Data 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 | (props: ParentProps<'span'>) => Element Use 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 | (props: ParentProps<'label'>) => Element Use 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 |
List
Prop | Default | Type |
---|---|---|
asChild | (props: ParentProps<'div'>) => Element Use 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 | (props: ParentProps<'div'>) => Element Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
RootProvider
Prop | Default | Type |
---|---|---|
value | UseSelectReturn<T> | |
asChild | (props: ParentProps<'div'>) => Element Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | boolean Whether to enable lazy mounting |
onExitComplete | () => void Function called when the animation ends in the closed state | |
present | boolean Whether the node is present (controlled by the user) | |
unmountOnExit | false | boolean Whether to unmount on exit. |
Trigger
Prop | Default | Type |
---|---|---|
asChild | (props: ParentProps<'button'>) => Element Use 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 | (props: ParentProps<'span'>) => Element Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
placeholder | string Text to display when no value is selected. |
Data Attribute | Value |
---|---|
[data-scope] | select |
[data-part] | value-text |
[data-disabled] | Present when disabled |
[data-invalid] | Present when invalid |
[data-focus] | Present when focused |
Accessibility
Complies with the Listbox WAI-ARIA design pattern.
Keyboard Support
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. |