# Select

URL: https://ark-ui.com/docs/components/select
LLM: https://ark-ui.com/llms.txt/components/select

Displays a list of options for the user to pick from.

---



## Anatomy



```tsx
<Select.Root>
  <Select.Label />
  <Select.Control>
    <Select.Trigger>
      <Select.ValueText />
    </Select.Trigger>
    <Select.ClearTrigger />
    <Select.Indicator />
  </Select.Control>
  <Select.Positioner>
    <Select.Content>
      <Select.ItemGroup>
        <Select.ItemGroupLabel />
        <Select.Item>
          <Select.ItemText />
          <Select.ItemIndicator />
        </Select.Item>
      </Select.ItemGroup>
    </Select.Content>
  </Select.Positioner>
  <Select.HiddenSelect />
</Select.Root>
```

## Examples

```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 (
    <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>
  )
}
```

### Controlled

Use the `value` and `onValueChange` props to control the selected items.

```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<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>
  )
}
```

### 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.

```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 (
    <>
      <output>selected: {JSON.stringify(select.value)}</output>

      <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>
    </>
  )
}
```

### Multiple

To enable `multiple` item selection:

```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 (
    <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.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>
  )
}
```

### 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.

```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 (
    <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>
  )
}
```

### Field

Use `Field` to manage form state, ARIA labels, helper text, and error text.

```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 (
    <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>
  )
}
```

### Form Usage

Here's an example of integrating the `Select` component with a form library.

```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<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.Trigger>
              <div className={styles.Indicators}>
                <Select.ClearTrigger className={styles.ClearTrigger}>
                  <XIcon />
                </Select.ClearTrigger>
                <Select.Indicator className={styles.Indicator}>
                  <ChevronsUpDownIcon />
                </Select.Indicator>
              </div>
            </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>
  )
}
```

### Async Loading

Here's an example of how to load the items asynchronously when the select is opened.

```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<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>
  )
}
```

### Lazy Mount

Use `lazyMount` and `unmountOnExit` to control when content is mounted, improving performance.

```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 (
    <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>
  )
}
```

### Select on Highlight

Here's an example of automatically selecting items when they are highlighted (hovered or navigated to with keyboard).

```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 (
    <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>
  )
}
```

### Max Selection

Here's an example of limiting the number of items that can be selected in a multiple select.

```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<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>
  )
}
```

### Select All

Use `selectAll()` from the select context to select all items at once.

```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 (
    <Select.Context>
      {(api) => (
        <button
          className={button.Root}
          style={{ width: '100%', marginBottom: '0.25rem' }}
          type="button"
          onClick={() => {
            api.selectAll()
            api.setOpen(false)
          }}
        >
          Select All
        </button>
      )}
    </Select.Context>
  )
}

export const SelectAll = () => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })

  return (
    <Select.Root className={styles.Root} collection={collection}>
      <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}>
            <SelectAllButton />
            {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>
  )
}
```

### 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.

```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 (
    <Select.Root
      className={styles.Root}
      collection={collection}
      positioning={{
        fitViewport: true,
        placement: 'bottom-start',
        sameWidth: true,
      }}
    >
      <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} style={{ maxHeight: '200px' }}>
            <Select.ItemGroup className={styles.ItemGroup}>
              <Select.ItemGroupLabel className={styles.ItemGroupLabel}>Names</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>
  )
}
```

## 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 <ArkSelect.Root {...props}>{/* ... */}</ArkSelect.Root>
}
```

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 (
    <Select
      collection={collection}
      onValueChange={(e) => {
        // e.items is typed as Array<{ label: string, value: string }>
        console.log(e.items)
      }}
    >
      {/* ... */}
    </Select>
  )
}
```

### 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

```tsx
<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.

### Virtualization

For virtualized lists with many items, avoid using `Select.HiddenSelect` as it renders an `<option>` for every item in
the collection. Instead, create a lightweight hidden input:

```tsx
import { Select, useSelectContext } from '@ark-ui/react/select'

const SelectHiddenInput = ({ name }: { name: string }) => {
  const select = useSelectContext()
  return (
    <>
      {select.value.map((value) => (
        <input key={value} type="hidden" name={name} value={value} />
      ))}
    </>
  )
}
```

### 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:

```tsx
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:

```tsx
<Select.Content>
  <SelectEmpty>No items to display</SelectEmpty>
  {/* Your items */}
</Select.Content>
```

### Available Size

The following css variables are exposed to the `Select.Positioner` which you can use to style the `Select.Content`

```css
/* 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:

```css
[data-scope='select'][data-part='content'] {
  max-height: calc(var(--available-height) - 100px);
}
```

## API Reference

### Props

### Root

#### Props

**`collection`**
Type: `ListCollection<T>`
Required: true
Default Value: `undefined`
Description: The collection of items

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`autoComplete`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The autocomplete attribute for the hidden select. Enables browser autofill (e.g. "address-level1" for state).

**`closeOnSelect`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the select should close after an item is selected

**`composite`**
Type: `boolean`
Required: false
Default Value: `true`
Description: Whether the select is a composed with other composite widgets like tabs or combobox

**`defaultHighlightedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The initial value of the highlighted item when opened.
Use when you don't need to control the highlighted value of the select.

**`defaultOpen`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select's open state is controlled by the user

**`defaultValue`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The initial default value of the select when rendered.
Use when you don't need to control the value of the select.

**`deselectable`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the value can be cleared by clicking the selected item.

**Note:** this is only applicable for single selection

**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is disabled

**`form`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The associate form of the underlying select.

**`hideMode`**
Type: `HideMode`
Required: false
Default Value: `'display-none'`
Description: How to hide content when mounted but not present.
- `'display-none'`: HTML `hidden` attribute. Effects stay alive.
- `'activity'`: React 19 `<Activity mode="hidden">`. Effects pause. Requires React 19+.

**`highlightedValue`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The controlled key of the highlighted item

**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.

**`ids`**
Type: `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
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the select. Useful for composition.

**`immediate`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to synchronize the present change immediately or defer it to the next frame

**`invalid`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is invalid

**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting

**`loopFocus`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to loop the keyboard navigation through the options

**`multiple`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to allow multiple selection

**`name`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The `name` attribute of the underlying select.

**`onExitComplete`**
Type: `VoidFunction`
Required: false
Default Value: `undefined`
Description: Function called when the animation ends in the closed state

**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when the focus is moved outside the component

**`onHighlightChange`**
Type: `(details: HighlightChangeDetails<T>) => void`
Required: false
Default Value: `undefined`
Description: The callback fired when the highlighted item changes.

**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when an interaction happens outside the component

**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the popup is opened

**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Required: false
Default Value: `undefined`
Description: Function called when the pointer is pressed down outside the component

**`onSelect`**
Type: `(details: SelectionDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when an item is selected

**`onValueChange`**
Type: `(details: ValueChangeDetails<T>) => void`
Required: false
Default Value: `undefined`
Description: The callback fired when the selected item changes.

**`open`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select menu is open

**`positioning`**
Type: `PositioningOptions`
Required: false
Default Value: `undefined`
Description: The positioning options of the menu.

**`present`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the node is present (controlled by the user)

**`readOnly`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is read-only

**`required`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the select is required

**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Required: false
Default Value: `undefined`
Description: Function to scroll to a specific index

**`skipAnimationOnMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to allow the initial presence animation.

**`translations`**
Type: `IntlTranslations`
Required: false
Default Value: `undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states

**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.

**`value`**
Type: `string[]`
Required: false
Default Value: `undefined`
Description: The controlled keys of the selected items

#### Data Attributes

**`data-scope`**: select
**`data-part`**: root
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only

### ClearTrigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: select
**`data-part`**: clear-trigger
**`data-invalid`**: Present when invalid

### Content

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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-side`**: The side of the trigger that the content is positioned on
**`data-activedescendant`**: The id the active descendant of the content

### Control

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Indicator

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### ItemGroup

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: select
**`data-part`**: item-group
**`data-disabled`**: Present when disabled

### ItemIndicator

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: select
**`data-part`**: item-indicator
**`data-state`**: "checked" | "unchecked"

### Item

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`item`**
Type: `any`
Required: false
Default Value: `undefined`
Description: The item to render

**`persistFocus`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether hovering outside should clear the highlighted state

#### Data Attributes

**`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

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: select
**`data-part`**: item-text
**`data-state`**: "checked" | "unchecked"
**`data-disabled`**: Present when disabled
**`data-highlighted`**: Present when highlighted

### Label

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Positioner

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### RootProvider

#### Props

**`value`**
Type: `UseSelectReturn<T>`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`hideMode`**
Type: `HideMode`
Required: false
Default Value: `'display-none'`
Description: How to hide content when mounted but not present.
- `'display-none'`: HTML `hidden` attribute. Effects stay alive.
- `'activity'`: React 19 `<Activity mode="hidden">`. Effects pause. Requires React 19+.

**`immediate`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to synchronize the present change immediately or defer it to the next frame

**`lazyMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to enable lazy mounting

**`onExitComplete`**
Type: `VoidFunction`
Required: false
Default Value: `undefined`
Description: Function called when the animation ends in the closed state

**`present`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether the node is present (controlled by the user)

**`skipAnimationOnMount`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to allow the initial presence animation.

**`unmountOnExit`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to unmount on exit.

### Trigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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-side`**: The side of the trigger that the trigger is positioned on
**`data-placeholder-shown`**: Present when placeholder is shown

### ValueText

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`placeholder`**
Type: `string`
Required: false
Default Value: `undefined`
Description: Text to display when no value is selected.

#### Data Attributes

**`data-scope`**: select
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused

### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `focused` | `boolean` | Whether the select is focused |
| `open` | `boolean` | Whether the select is open |
| `empty` | `boolean` | Whether the select value is empty |
| `highlightedValue` | `string | null` | The value of the highlighted item |
| `highlightedItem` | `V | null` | The highlighted item |
| `setHighlightValue` | `(value: string) => void` | Function to highlight a value |
| `clearHighlightValue` | `VoidFunction` | Function to clear the highlighted value |
| `selectedItems` | `V[]` | The selected items |
| `hasSelectedItems` | `boolean` | Whether there's a selected option |
| `value` | `string[]` | The selected item keys |
| `valueAsString` | `string` | The string representation of the selected items |
| `selectValue` | `(value: string) => void` | Function to select a value |
| `selectAll` | `VoidFunction` | Function to select all values |
| `setValue` | `(value: string[]) => void` | Function to set the value of the select |
| `clearValue` | `(value?: string) => void` | Function 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` | `VoidFunction` | Function to focus on the select input |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of a select item |
| `setOpen` | `(open: boolean) => void` | Function to open or close the select |
| `collection` | `ListCollection<V>` | Function to toggle the select |
| `reposition` | `(options?: Partial<PositioningOptions>) => void` | Function to set the positioning options of the select |
| `multiple` | `boolean` | Whether the select allows multiple selections |
| `disabled` | `boolean` | Whether the select is disabled |


## Accessibility

Complies with the [Listbox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/).

### Keyboard Support

**`Space`**
Description: <span>When focus is on trigger, opens the select and focuses the first selected item.<br />When focus is on the content, selects the highlighted item.</span>

**`Enter`**
Description: <span>When focus is on trigger, opens the select and focuses the first selected item.<br />When focus is on content, selects the focused item.</span>

**`ArrowDown`**
Description: <span>When focus is on trigger, opens the select.<br />When focus is on content, moves focus to the next item.</span>

**`ArrowUp`**
Description: <span>When focus is on trigger, opens the select.<br />When focus is on content, moves focus to the previous item.</span>

**`Esc`**
Description: <span>Closes the select and moves focus to trigger.</span>

**`A-Z + a-z`**
Description: <span>When focus is on trigger, selects the item whose label starts with the typed character.<br />When focus is on the listbox, moves focus to the next item with a label that starts with the typed character.</span>