---
title: Select
description: Choose one or more values from a popup list of options.
---
import { StoryEmbed } from '/snippets/ui/StoryEmbed.mdx';
import SelectReference from '/snippets/ui/generated/input/select.mdx';
Use `Select` for a form value chosen from a list. Use [Menu](/ui/primitives/surfaces/menu) for commands and actions.
## Anatomy
Keep the trigger and popup inside the same `Select.Root`. Put option labels directly inside `Select.Item`.
```text
Select.Root
├── Select.Trigger
│ └── Select.Value
└── Select.Popup
└── Select.Item
```
| Part | Requirement | Purpose |
| --------------------- | ----------------------------------- | ------------------------------------------------------------------------------------ |
| `Root` | Required | Owns selection, open state, and form integration. |
| `Trigger` | Required for a trigger-based select | Opens the list and displays its current value. |
| `Value` | Optional | Renders the selected label or a placeholder; you can provide custom trigger content. |
| `Popup` | Required for the option list | Renders the positioned, portaled list. |
| `Item` | One per option | Provides selection behavior and an option label. |
| `Group`, `GroupLabel` | Optional | Group related options under a label. |
| `Separator` | Optional | Separates groups visually. |
Twenty UI includes the chevron in `Trigger`, the option text and checkmark in `Item`, and the portal and positioner in `Popup`. Compose these public parts directly; placement and portal options belong on `Popup`.
## Uncontrolled state
Use `defaultValue` to set the initial priority. The select owns subsequent selection changes.
```tsx
import { Select } from 'twenty-ui/primitives/input';
const PRIORITIES = [
{ value: 'low', label: 'Low' },
{ value: 'normal', label: 'Normal' },
{ value: 'high', label: 'High' },
];
export const PrioritySelect = () => (
{PRIORITIES.map(({ value, label }) => (
{label}
))}
);
```
Supply `items` on `Root` so `Value` renders the selected item's label; without it, the trigger shows the raw value. The selected value can be `null` when nothing is selected.
## Controlled state
Pass `value` and update it with `onValueChange` to own the same priority in React state. The callback receives the next value and event details.
```tsx
import { useState } from 'react';
import { Select } from 'twenty-ui/primitives/input';
const PRIORITIES = [
{ value: 'low', label: 'Low' },
{ value: 'normal', label: 'Normal' },
{ value: 'high', label: 'High' },
];
export const PrioritySelect = () => {
const [priority, setPriority] = useState('normal');
return (
{PRIORITIES.map(({ value, label }) => (
{label}
))}
);
};
```
## Multiple selection
```tsx
import { useState } from 'react';
import { Select } from 'twenty-ui/primitives/input';
const LANGUAGES = [
{ value: 'en', label: 'English' },
{ value: 'fr', label: 'French' },
{ value: 'es', label: 'Spanish' },
];
export const LanguageSelect = () => {
const [languages, setLanguages] = useState(['en']);
return (
{LANGUAGES.map(({ value, label }) => (
{label}
))}
);
};
```
With `multiple`, the value is an array. Use `defaultValue={['en']}` instead of `value` and `onValueChange` for an uncontrolled multiple selection. The generic root types represent the option value and whether multiple selection is enabled.
## Open state
Selection and popup visibility are independent. The examples above leave popup visibility uncontrolled. Selecting a single value closes the popup; multiple selection keeps it open. Use `defaultOpen` to set its initial visibility; it defaults to `false`.
Pass `open` and update it with `onOpenChange` to control visibility. This example controls the popup while leaving the selected priority uncontrolled.
```tsx
import { useState } from 'react';
import { Select } from 'twenty-ui/primitives/input';
const PRIORITIES = [
{ value: 'low', label: 'Low' },
{ value: 'normal', label: 'Normal' },
{ value: 'high', label: 'High' },
];
export const PrioritySelect = () => {
const [open, setOpen] = useState(false);
return (
{PRIORITIES.map(({ value, label }) => (
{label}
))}
);
};
```
## Compose with a field
Wrap the select in `Field.Root` to associate its trigger with a visible label and helper text. The field handles labeling and validation; the select keeps its own selection and popup state.
```tsx
import { Field, Select } from 'twenty-ui/primitives/input';
const PRIORITIES = [
{ value: 'low', label: 'Low' },
{ value: 'normal', label: 'Normal' },
{ value: 'high', label: 'High' },
];
export const PriorityField = () => (
Priority
{PRIORITIES.map(({ value, label }) => (
{label}
))}
Choose how soon this record needs attention.
);
```
## Placement and keyboard
Set `size="sm"` or `size="md"` on `Trigger`. Customize option content with `Item.startIcon`, `Item.endIcon`, and `Item.description` while keeping the option's label in its children.
`Popup` defaults to `side="bottom"`, `align="start"`, and `sideOffset={8}`. Set `alignItemWithTrigger` to align the selected option over the trigger. It uses the [theme's portal container](/ui/theming#scope-an-override) unless you provide `container`.
Keyboard users can open the list from the trigger, navigate options with arrow keys, and type to find an option. Escape dismisses the popup. Label the trigger or integrate the select with [Field](/ui/primitives/input/field); a placeholder alone does not name the control.
## Props
The reference is generated from the public component types. Native attributes, including accessible names and event handlers, are also accepted on parts that render elements.