# Select URL: https://ark-ui.com/docs/components/select Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/select.mdx Displays a list of options for the user to pick from. --- ## Anatomy ```tsx ``` ## Examples **Example: basic** #### React ```tsx 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 ( Framework
Frameworks {frameworks.items.map((item) => ( {item.label} ))}
) } ``` #### Solid ```tsx 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 ( Framework
Frameworks {(item) => ( {item().label} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework
Frameworks {#each frameworks.items as item} {item.label} {/each}
``` ### Controlled Use the `value` and `onValueChange` props to control the selected items. **Example: controlled** #### React ```tsx 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([]) const collection = createListCollection({ items: [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, { label: 'Svelte', value: 'svelte', disabled: true }, ], }) const handleValueChange = (details: Select.ValueChangeDetails) => { setValue(details.value) } return ( Framework
Frameworks {collection.items.map((item) => ( {item.label} ))}
) } ``` #### Solid ```tsx 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([]) const collection = createListCollection({ items: [ { label: 'React', value: 'react' }, { label: 'Solid', value: 'solid' }, { label: 'Vue', value: 'vue' }, { label: 'Svelte', value: 'svelte', disabled: true }, ], }) const handleValueChange = (details: Select.ValueChangeDetails) => { setValue(details.value) } return ( Framework
Frameworks {(item) => ( {item().label} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework
Frameworks {#each collection.items as item (item.value)} {item.label} {/each}
``` ### Root Provider An alternative way to control the select is to use the `RootProvider` component and the `useSelect` hook. This way you can access the state and methods from outside the component. **Example: root-provider** #### React ```tsx import { Portal } from '@ark-ui/react/portal' import { Select, createListCollection, useSelect } 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 RootProvider = () => { const select = useSelect({ collection: frameworks }) return ( <> selected: {JSON.stringify(select.value)} Framework
Frameworks {frameworks.items.map((item) => ( {item.label} ))}
) } ``` #### Solid ```tsx import { Select, createListCollection, useSelect } 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 RootProvider = () => { const select = useSelect({ collection: frameworks }) return ( <> selected: {JSON.stringify(select().value)} Framework
Frameworks {(item) => ( {item().label} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
Framework
Frameworks {#each frameworks.items as item} {item.label} {/each}
``` ### Multiple To enable `multiple` item selection: **Example: multiple** #### React ```tsx 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 ( Framework
Frameworks {frameworks.items.map((item) => ( {item.label} ))}
) } ``` #### Solid ```tsx 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 ( Framework
Frameworks {(item) => ( {item().label} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework
Frameworks {#each frameworks.items as item (item.value)} {item.label} {/each}
``` ### Grouping Grouping related options can be useful for organizing options into categories. - Use the `groupBy` prop to configure the grouping of the items. - Use the `collection.group()` method to get the grouped items. - Use the `Select.ItemGroup` and `Select.ItemGroupLabel` components to render the grouped items. **Example: grouping** #### React ```tsx 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 ( Framework
{frameworks.group().map(([type, group]) => ( {type} {group.map((item) => ( {item.label} ))} ))}
) } ``` #### Solid ```tsx 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 ( Framework
{([type, group]) => ( {type} {(item) => ( {item.label} )} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework
{#each frameworks.group() as [type, group]} {type} {#each group as item (item.value)} {item.label} {/each} {/each}
``` ### Field Use `Field` to manage form state, ARIA labels, helper text, and error text. **Example: with-field** #### React ```tsx 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 ( Label {collection.items.map((item) => ( {item} ))} Additional Info Error Info ) } ``` #### Solid ```tsx 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 ( Label {(item) => ( {item()} )} Additional Info Error Info ) } ``` #### Vue ```vue ``` #### Svelte ```svelte Label {#each collection.items as item} {item} {/each} Additional Info Error Info ``` ### Form Usage Here's an example of integrating the `Select` component with a form library. **Example: form-library** #### React ```tsx import { Select, createListCollection } from '@ark-ui/react/select' import { ChevronsUpDownIcon, XIcon } from 'lucide-react' import { Controller, type SubmitHandler, useForm } from 'react-hook-form' import button from 'styles/button.module.css' import styles from 'styles/select.module.css' interface Inputs { framework: string } export const FormLibrary = () => { const { control, handleSubmit } = useForm({ defaultValues: { framework: 'React' }, }) const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'], }) const onSubmit: SubmitHandler = (data) => { window.alert(JSON.stringify(data)) } return (
( field.onChange(e.value[0])} name={field.name} onInteractOutside={() => field.onBlur()} > Framework
Frameworks {collection.items.map((item) => ( {item} ))}
)} /> ) } ``` #### Solid ```tsx import { Select, createListCollection } from '@ark-ui/solid/select' import { ChevronsUpDownIcon, XIcon } 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 ( <>
Value is {value()}
{ window.alert(JSON.stringify(data)) }} > {(field, props) => ( setValue(formStore, field.name, e.value[0])} > Framework
Frameworks {(item) => ( {item().label} )}
)}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte
Value is {formData.framework}
{ e.preventDefault() form.handleSubmit() }} > {#snippet children(field)} {@const state = field.state} 0} name={field.name} onValueChange={(details) => { field.handleChange(details.value[0]) }} > Framework
Frameworks {#each frameworks.items as item} {item.label} {/each}
{/snippet}
``` ### Async Loading Here's an example of how to load the items asynchronously when the select is opened. **Example: async** #### React ```tsx 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((resolve) => { setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500) }) } export const Async = () => { const [items, setItems] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const collection = createListCollection({ 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 ( Framework {loading ? (
Loading...
) : error ? (
Error: {error.message}
) : ( collection.items.map((item) => ( {item} )) )}
) } ``` #### Solid ```tsx 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((resolve) => { setTimeout(() => resolve(['React', 'Solid', 'Vue', 'Svelte', 'Angular', 'Ember']), 500) }) } export const Async = () => { const [items, setItems] = createSignal(null) const [loading, setLoading] = createSignal(false) const [error, setError] = createSignal(null) const collection = createMemo(() => createListCollection({ 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 ( Framework
Loading...
Error: {error()?.message}
{(item) => ( {item()} )}
) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework {#if loading}
Loading...
{:else if error}
Error: {error.message}
{:else} {#each collection.items as item} {item} {/each} {/if}
``` ### Lazy Mount Use `lazyMount` and `unmountOnExit` to control when content is mounted, improving performance. **Example: lazy-mount** #### React ```tsx 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 ( Framework Clear Frameworks {collection.items.map((item) => ( {item} ))} ) } ``` #### Solid ```tsx 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 ( Framework Clear Frameworks {(item) => ( {item()} )} ) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework Clear Frameworks {#each collection.items as item} {item} {/each} ``` ### Select on Highlight Here's an example of automatically selecting items when they are highlighted (hovered or navigated to with keyboard). **Example: select-on-highlight** #### React ```tsx 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 ( Framework Clear Frameworks {collection.items.map((item) => ( {item} ))} ) } ``` #### Solid ```tsx 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 ( Framework Clear Frameworks {(item) => ( {item()} )} ) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework Clear Frameworks {#each frameworks.items as item} {item.label} {/each} ``` ### Max Selection Here's an example of limiting the number of items that can be selected in a multiple select. **Example: max-selected** #### React ```tsx 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([]) 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 ( Framework Frameworks {collection.items.map((item) => ( {item.label} ))} ) } ``` #### Solid ```tsx 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([]) 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 ( Framework Frameworks {(item) => ( {item().label} )} ) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework Frameworks {#each collection.items as item (item.value)} {item.label} {/each} ``` ### Select All Use `selectAll()` from the select context to select all items at once. **Example: select-all** #### React ```tsx 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' import button from 'styles/button.module.css' const SelectAllButton = () => { return ( {(api) => ( )} ) } export const SelectAll = () => { const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] }) return ( Framework Clear {collection.items.map((item) => ( {item} ))} ) } ``` #### Solid ```tsx 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' import button from 'styles/button.module.css' const SelectAllButton = () => { return ( {(api) => ( )} ) } export const SelectAll = () => { const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] }) return ( Framework Clear {(item) => ( {item()} )} ) } ``` #### Vue ```vue ``` #### Svelte ```svelte Framework Clear {#snippet render(api)} {/snippet} {#each collection.items as item} {item} {/each} ``` ### Overflow For selects with many items, use `positioning.fitViewport` to ensure the dropdown fits within the viewport. Combine with a max-height on the content to enable scrolling. **Example: overflow** #### React ```tsx 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 Overflow = () => { const collection = createListCollection({ items: [ 'Name 1', 'Name 2', 'Name 3', 'Name 4', 'Name 5', 'Name 6', 'Name 7', 'Name 8', 'Name 9', 'Name 10', 'Name 11', 'Name 12', 'Name 13', 'Name 14', ], }) return ( Framework Clear Names {collection.items.map((item) => ( {item} ))} ) } ``` #### Solid ```tsx 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' const collection = createListCollection({ items: [ 'Name 1', 'Name 2', 'Name 3', 'Name 4', 'Name 5', 'Name 6', 'Name 7', 'Name 8', 'Name 9', 'Name 10', 'Name 11', 'Name 12', 'Name 13', 'Name 14', ], }) export const Overflow = () => ( Framework Clear Names {(item) => ( {item()} )} ) ``` #### Vue ```vue ``` #### Svelte ```svelte Framework Clear Names {#each collection.items as item} {item} {/each} ``` ## Guides ### Type Safety The `Select.RootComponent` type enables you to create typed wrapper components that maintain full type safety for collection items. ```tsx const Select: ArkSelect.RootComponent = (props) => { return {/* ... */} } ``` Use the wrapper with full type inference on `onValueChange` and other callbacks: ```tsx const App = () => { const collection = createListCollection({ initialItems: [ { label: 'React', value: 'react' }, { label: 'Vue', value: 'vue' }, ], }) return ( ) } ``` ### Hidden Select The `Select.HiddenSelect` component renders a native HTML `` 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 ```tsx {/* Other Select components */} ``` The hidden select automatically syncs with the Select component's value, ensuring form data is always up-to-date. ### Virtualization For virtualized lists with many items, avoid using `Select.HiddenSelect` as it renders an `