# Select

Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
} from "@/components/starwind/select";

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
} from "@/components/starwind/select";
---

<Select name="framework" defaultValue="astro" required>
  <SelectTrigger placeholder="Choose a framework" />
  <SelectContent>
    <SelectItem value="astro">Astro</SelectItem>
    <SelectItem value="react">React</SelectItem>
    <SelectItem value="vue">Vue</SelectItem>
  </SelectContent>
</Select>
```
  </div>
  <div slot="react">
```tsx
import {
      Select,
      SelectContent,
      SelectItem,
      SelectTrigger,
    } from "@/components/starwind/select";

export function Example() {
  return (
    <>
      <Select name="framework" defaultValue="astro" required>
        <SelectTrigger placeholder="Choose a framework" />
        <SelectContent>
          <SelectItem value="astro">Astro</SelectItem>
          <SelectItem value="react">React</SelectItem>
          <SelectItem value="vue">Vue</SelectItem>
        </SelectContent>
      </Select>
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
</script>

<template>
  <Select name="framework" defaultValue="astro" required>
    <SelectTrigger placeholder="Choose a framework" />
    <SelectContent>
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="react">React</SelectItem>
      <SelectItem value="vue">Vue</SelectItem>
    </SelectContent>
  </Select>
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Installation

```bash
npx starwind@latest add select --framework astro
```
</DocsTabsContent>
```bash
npx starwind@latest add select --framework react
```
</DocsTabsContent>
```bash
npx starwind@latest add select --framework vue
```
</DocsTabsContent>
</DocsTabs>

## Usage

<FrameworkCodeSwitcher>
<div slot="astro">
```astro
---
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
---

<Select name="framework">
  <SelectTrigger placeholder="Choose a framework" />
  <SelectContent>
    <SelectItem value="astro">Astro</SelectItem>
    <SelectItem value="react">React</SelectItem>
  </SelectContent>
</Select>
```
</div>
<div slot="react">
```tsx
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";

export function Example() {
  return (
    <Select name="framework">
      <SelectTrigger placeholder="Choose a framework" />
      <SelectContent>
        <SelectItem value="astro">Astro</SelectItem>
        <SelectItem value="react">React</SelectItem>
      </SelectContent>
    </Select>
  );
}
```
</div>
<div slot="vue">
```vue
<script setup lang="ts">
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
</script>

<template>
  <Select name="framework">
    <SelectTrigger placeholder="Choose a framework" />
    <SelectContent>
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="react">React</SelectItem>
    </SelectContent>
  </Select>
</template>
```
</div>
</FrameworkCodeSwitcher>

## Composition

Use this structure for a Select with grouped options:

```text
Select
├── SelectTrigger
└── SelectContent
    ├── SelectGroup
    │   ├── SelectLabel
    │   └── SelectItem
    ├── SelectSeparator
    └── SelectItem
```

## Sizes

Set `size` on `SelectTrigger` and `SelectContent` separately. Both accept `sm`, `md` (the default),
or `lg`. Set both to the same value for a consistent control, or use a larger content size when the
popup needs more room.

<FrameworkCodeSwitcher>
<div slot="astro">
```astro
---
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
---

{(["sm", "md", "lg"] as const).map((size) => (
  <Select defaultValue="astro">
    <SelectTrigger size={size} placeholder={`${size} trigger`} />
    <SelectContent size={size}>
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="react">React</SelectItem>
      <SelectItem value="vue">Vue</SelectItem>
    </SelectContent>
  </Select>
))}

<Select defaultValue="astro">
  <SelectTrigger size="sm" placeholder="Compact trigger" />
  <SelectContent size="lg">
    <SelectItem value="astro">Astro</SelectItem>
    <SelectItem value="react">React</SelectItem>
    <SelectItem value="vue">Vue</SelectItem>
  </SelectContent>
</Select>
```
</div>
<div slot="react">
```tsx
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";

export function Example() {
  return (
    <>
      {(["sm", "md", "lg"] as const).map((size) => (
        <Select key={size} defaultValue="astro">
          <SelectTrigger size={size} placeholder={`${size} trigger`} />
          <SelectContent size={size}>
            <SelectItem value="astro">Astro</SelectItem>
            <SelectItem value="react">React</SelectItem>
            <SelectItem value="vue">Vue</SelectItem>
          </SelectContent>
        </Select>
      ))}

      <Select defaultValue="astro">
        <SelectTrigger size="sm" placeholder="Compact trigger" />
        <SelectContent size="lg">
          <SelectItem value="astro">Astro</SelectItem>
          <SelectItem value="react">React</SelectItem>
          <SelectItem value="vue">Vue</SelectItem>
        </SelectContent>
      </Select>
    </>
  );
}
```
</div>
<div slot="vue">
```vue
<script setup lang="ts">
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
const sizes = ["sm", "md", "lg"] as const;
</script>

<template>
  <template v-for="size in sizes" :key="size">
    <Select defaultValue="astro">
      <SelectTrigger :size="size" :placeholder="`${{ size }} trigger`" />
      <SelectContent :size="size">
        <SelectItem value="astro">Astro</SelectItem>
        <SelectItem value="react">React</SelectItem>
        <SelectItem value="vue">Vue</SelectItem>
      </SelectContent>
    </Select>
  </template>

  <Select defaultValue="astro">
    <SelectTrigger size="sm" placeholder="Compact trigger" />
    <SelectContent size="lg">
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="react">React</SelectItem>
      <SelectItem value="vue">Vue</SelectItem>
    </SelectContent>
  </Select>
</template>
```
</div>
</FrameworkCodeSwitcher>

## Form Participation

Select serializes its value through a hidden input and follows native form reset behavior.

<FrameworkCodeSwitcher>
<div slot="astro">
```astro
---
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
---

<form>
  <Select name="role" required>
    <SelectTrigger placeholder="Choose a role" />
    <SelectContent>
      <SelectItem value="admin">Administrator</SelectItem>
      <SelectItem value="member">Member</SelectItem>
    </SelectContent>
  </Select>
  <button type="submit">Save</button>
</form>
```
</div>
<div slot="react">
```tsx
import { useState } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";

export function Example() {
  const [submittedRole, setSubmittedRole] = useState<string | null>(null);

  return (
      <form
        onSubmit={(event) => {
          event.preventDefault();
          const data = new FormData(event.currentTarget);
          setSubmittedRole(String(data.get("role") ?? ""));
        }}
      >
        <Select name="role" required>
          <SelectTrigger placeholder="Choose a role" />
          <SelectContent>
            <SelectItem value="admin">Administrator</SelectItem>
            <SelectItem value="member">Member</SelectItem>
          </SelectContent>
        </Select>
        <button type="submit">Save</button>
        {submittedRole !== null && (
          <output aria-live="polite">Submitted role: {submittedRole}</output>
        )}
      </form>
  );
}
```
</div>
<div slot="vue">
```vue
<script setup lang="ts">
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
</script>

<template>
  <form>
    <Select name="role" required>
      <SelectTrigger placeholder="Choose a role" />
      <SelectContent>
        <SelectItem value="admin">Administrator</SelectItem>
        <SelectItem value="member">Member</SelectItem>
      </SelectContent>
    </Select>
    <button type="submit">Save</button>
  </form>
</template>
```
</div>
</FrameworkCodeSwitcher>

## Programmatic Value Changes

Dispatch `starwind:set-value` on the Select root. Listen for the root-scoped
`starwind:value-change` event when the selected value changes.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
---

<Select id="role-select" name="role">
  <SelectTrigger placeholder="Choose a role" />
  <SelectContent>
    <SelectItem value="admin">Administrator</SelectItem>
    <SelectItem value="member">Member</SelectItem>
  </SelectContent>
</Select>

<button id="choose-admin" type="button">Choose administrator</button>

<script>
  const select = document.querySelector<HTMLElement>("#role-select");
  const button = document.querySelector<HTMLElement>("#choose-admin");

  button?.addEventListener("click", () => {
    select?.dispatchEvent(
      new CustomEvent("starwind:set-value", {
        detail: { value: "admin" },
      }),
    );
  });

  select?.addEventListener("starwind:value-change", (event) => {
    const { value } = (event as CustomEvent<{ value: string | null }>).detail;
    console.log(value);
  });
</script>
```
  </div>
  <div slot="react">
```tsx
import { useState } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";

export function Example() {
  const [value, setValue] = useState<string | null>(null);

  return (
    <>
      <Select
        name="role"
        value={value}
        onValueChange={(nextValue) => {
          setValue(nextValue);
          console.log(nextValue);
        }}
      >
        <SelectTrigger placeholder="Choose a role" />
        <SelectContent>
          <SelectItem value="admin">Administrator</SelectItem>
          <SelectItem value="member">Member</SelectItem>
        </SelectContent>
      </Select>
      <button type="button" onClick={() => setValue("admin")}>
        Choose administrator
      </button>
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
import { ref } from "vue";
const role = ref<string | null>(null);
</script>

<template>
  <Select v-model="role" @value-change="(value) => console.log(value)" id="role-select" name="role">
    <SelectTrigger placeholder="Choose a role" />
    <SelectContent>
      <SelectItem value="admin">Administrator</SelectItem>
      <SelectItem value="member">Member</SelectItem>
    </SelectContent>
  </Select>

  <button @click="role = 'admin'" type="button">Choose administrator</button>
</template>
```
  </div>
</FrameworkCodeSwitcher>

For a searchable text input with listbox suggestions, install [Combobox](/docs/components/combobox)
instead of adding the removed search part.

## API Reference
### Select
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `defaultOpen` | `boolean` | No | `false` | Primitive override | Sets the initial open state when the component is uncontrolled. |
| `disabled` | `boolean` | No | `false` | Primitive override | Disables interaction with the component. |
| `required` | `boolean` | No | `false` | Primitive override | Marks the control as required for form validation. |
- Inherits div attributes. Omits `defaultValue` and `onChange`.

### SelectTrigger
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `false` | Primitive override | Merges the component behavior and props into its child element. |
| `iconClass` | `string` | No | - | Wrapper prop | Adds classes to the component's generated icon. |
| `placeholder` | `string` | No | - | Wrapper prop | Provides fallback text for the value element generated inside the trigger. |
| `showIcon` | `boolean` | No | `true` | Wrapper prop | Shows the component's generated icon. |
| `size` | `"sm" \| "md" \| "lg"` | No | `"md"` | Styled variant | Selects the component's visual size. |
| `valueClass` | `string` | No | - | Wrapper prop | Adds classes to the generated value element. |
- Inherits button attributes.

### SelectValue
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `placeholder` | `string` | No | - | Wrapper prop | Provides fallback text when no value is available. |
- Inherits span attributes.

### SelectContent
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `disablePortal` | `boolean` | No | `false` | Wrapper prop | Keeps the public Portal wrapper inline instead of moving it to a target. |
| `portalContainer` | `string` | No | - | Wrapper prop | Sets the CSS selector for the public Portal wrapper target. |
| `size` | `"sm" \| "md" \| "lg"` | No | `"md"` | Wrapper prop | Selects the component's visual size. |
- Inherits div attributes.

### SelectItem
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `disabled` | `boolean` | No | `false` | Styled variant | Disables interaction with the component. |
| `indicatorClass` | `string` | No | - | Wrapper prop | Adds classes to the generated selection indicator. |
| `inset` | `boolean` | No | `false` | Styled variant | Adds leading inset spacing for visual alignment. |
| `showIndicator` | `boolean` | No | `true` | Wrapper prop | Shows the generated selection indicator. |
| `value` | `string` | Yes | - | Primitive override | Controls or identifies the component value. |
- Inherits div attributes. Omits `role`.

### SelectItemText
- Inherits span attributes.

### SelectItemIndicator
- Inherits span attributes.

### SelectGroup
- Inherits div attributes.

### SelectLabel
- Inherits div attributes.

### SelectSeparator
- Inherits div attributes.

### SelectScrollUpButton
- Inherits div attributes.

### SelectScrollDownButton
- Inherits div attributes.
### Primitive And Runtime API
Behavior, state, events, form participation, and imperative methods are documented in the lower-level references.
- Primitive: [Select Primitive](/docs/primitives/select/)
- Runtime factory: [`createSelect`](/docs/runtime/#create-select) from `@starwind-ui/runtime/select`

## Changelog

### v2.1.1

- Connected group headings to their groups with `aria-labelledby` so assistive technology can identify each group.

### v2.1.0

- Added `portalContainer` and `disablePortal` to `SelectContent` for custom portal targets and
inline rendering.
- Corrected touch interaction so tapping an open trigger closes the popup and tapping an item
selects its value on mobile browsers.

### v2.0.2

- Named the generated aggregate default export so React and Astro tooling can identify the installed component cleanly.

### v2.0.1

- `SelectTrigger` and `SelectContent` now expose their independently resolved size as
`data-size`, including the `md` default. No prop migration is required; set both props when the
control and portal should match.

### v2.0.0

- Rebuilt Select on Starwind Runtime for selection, forms, events, and scroll behavior; search is now provided by Combobox.
- See the [Select Primitive](/docs/primitives/select/) for the underlying unstyled anatomy and behavior API.