Select
---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>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> </> );}<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><script lang="ts"> import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";</script>
<Select name="framework" defaultValue="astro" required> <SelectTrigger aria-label="Choose a framework" placeholder="Choose a framework" /> <SelectContent> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="react">React</SelectItem> <SelectItem value="vue">Vue</SelectItem> </SelectContent></Select>Installation
pnpx starwind@latest add select --framework astronpx starwind@latest add select --framework astroyarn dlx starwind@latest add select --framework astropnpx starwind@latest add select --framework reactnpx starwind@latest add select --framework reactyarn dlx starwind@latest add select --framework reactpnpx starwind@latest add select --framework vuenpx starwind@latest add select --framework vueyarn dlx starwind@latest add select --framework vuepnpx starwind@latest add select --framework sveltenpx starwind@latest add select --framework svelteyarn dlx starwind@latest add select --framework svelteUsage
---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>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> );}<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><script lang="ts"> import { Select, SelectContent, SelectItem, SelectTrigger, } from "@/components/starwind/select";</script>
<Select name="framework"> <SelectTrigger placeholder="Choose a framework" /> <SelectContent> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="react">React</SelectItem> </SelectContent></Select>Composition
Use this structure for a Select with grouped options:
Select├── SelectTrigger└── SelectContent ├── SelectGroup │ ├── SelectLabel │ └── SelectItem ├── SelectSeparator └── SelectItemSizes
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.
---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>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> </> );}<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><script lang="ts"> import { Select, SelectContent, SelectItem, SelectTrigger, } from "@/components/starwind/select"; const sizes = ["sm", "md", "lg"] as const;</script>
{#each sizes as size (size)} <Select defaultValue="astro"> <SelectTrigger {size} placeholder={`${size} trigger`} /> <SelectContent {size}> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="react">React</SelectItem> <SelectItem value="vue">Vue</SelectItem> </SelectContent> </Select>{/each}<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>Form Participation
Select serializes its value through a hidden input and follows native form reset behavior.
---import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";---
<form id="select-role-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> <output aria-live="polite"></output></form>
<script> function initRoleForm() { const form = document.querySelector<HTMLFormElement>("#select-role-form"); if (!form) return; form.onsubmit = (event) => { event.preventDefault(); const role = String(new FormData(form).get("role") ?? ""); form.querySelector("output")!.textContent = role ? `Submitted role: ${role}` : "Choose a role before saving."; }; } initRoleForm(); document.addEventListener("astro:after-swap", initRoleForm);</script>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">{submittedRole ? `Submitted role: ${submittedRole}` : "Choose a role before saving."}</output> )} </form> );}<script setup lang="ts">import { ref } from "vue";import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";const submittedRole = ref<string | null>(null);function submitRole(event: Event) { const form = event.currentTarget as HTMLFormElement; submittedRole.value = String(new FormData(form).get("role") ?? "");}</script>
<template> <form @submit.prevent="submitRole"> <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> <output v-if="submittedRole !== null" aria-live="polite"> {{ submittedRole ? `Submitted role: ${submittedRole}` : "Choose a role before saving." }} </output> </form></template><script lang="ts"> import { Select, SelectContent, SelectItem, SelectTrigger, } from "@/components/starwind/select"; let submittedRole = $state<string | null>(null); function submitRole(event: SubmitEvent) { event.preventDefault(); const form = event.currentTarget as HTMLFormElement; submittedRole = String(new FormData(form).get("role") ?? ""); }</script>
<form onsubmit={submitRole}> <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> {#if submittedRole !== null} <output aria-live="polite"> {submittedRole ? `Submitted role: ${submittedRole}` : "Choose a role before saving."} </output> {/if}</form>The browser checks a required Select when the form submits. This works in a plain <form> with no Starwind Form or Field. Press Continue with no plan chosen. The browser blocks the submit and shows its message at the Select, and focus moves to the Select button. The submit handler runs only after you choose a plan.
---import { Button } from "@/components/starwind/button";import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";---
<form id="select-plan-form" class="grid w-full max-w-[16rem] gap-3"> <Select name="plan" required> <SelectTrigger class="w-full" placeholder="Choose a plan" /> <SelectContent> <SelectItem value="free">Free</SelectItem> <SelectItem value="pro">Pro</SelectItem> </SelectContent> </Select> <Button type="submit">Continue</Button> <output class="text-muted-foreground text-sm" aria-live="polite"></output></form>
<script> function initPlanForm() { const form = document.querySelector<HTMLFormElement>("#select-plan-form"); if (!form) return; form.onsubmit = (event) => { event.preventDefault(); form.querySelector("output")!.textContent = `Chosen plan: ${new FormData(form).get("plan")}`; }; } initPlanForm(); document.addEventListener("astro:after-swap", initPlanForm);</script>import { useState } from "react";import { Button } from "@/components/starwind/button";import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
export function Example() { const [plan, setPlan] = useState("");
return ( <form className="grid w-full max-w-[16rem] gap-3" onSubmit={(event) => { event.preventDefault(); setPlan(String(new FormData(event.currentTarget).get("plan"))); }} > <Select name="plan" required> <SelectTrigger className="w-full" placeholder="Choose a plan" /> <SelectContent> <SelectItem value="free">Free</SelectItem> <SelectItem value="pro">Pro</SelectItem> </SelectContent> </Select> <Button type="submit">Continue</Button> <output className="text-muted-foreground text-sm" aria-live="polite"> {plan && `Chosen plan: ${plan}`} </output> </form> );}<script setup lang="ts">import { ref } from "vue";import { Button } from "@/components/starwind/button";import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/starwind/select";
const plan = ref("");function submitPlan(event: Event) { plan.value = String(new FormData(event.currentTarget as HTMLFormElement).get("plan"));}</script>
<template> <form class="grid w-full max-w-[16rem] gap-3" @submit.prevent="submitPlan"> <Select name="plan" required> <SelectTrigger class="w-full" placeholder="Choose a plan" /> <SelectContent> <SelectItem value="free">Free</SelectItem> <SelectItem value="pro">Pro</SelectItem> </SelectContent> </Select> <Button type="submit">Continue</Button> <output class="text-muted-foreground text-sm" aria-live="polite"> {{ plan && `Chosen plan: ${plan}` }} </output> </form></template><script lang="ts"> import { Button } from "@/components/starwind/button"; import { Select, SelectContent, SelectItem, SelectTrigger, } from "@/components/starwind/select";
let plan = $state(""); function submitPlan(event: SubmitEvent) { event.preventDefault(); plan = String(new FormData(event.currentTarget as HTMLFormElement).get("plan")); }</script>
<form class="grid w-full max-w-[16rem] gap-3" onsubmit={submitPlan}> <Select name="plan" required> <SelectTrigger class="w-full" placeholder="Choose a plan" /> <SelectContent> <SelectItem value="free">Free</SelectItem> <SelectItem value="pro">Pro</SelectItem> </SelectContent> </Select> <Button type="submit">Continue</Button> <output class="text-muted-foreground text-sm" aria-live="polite"> {plan && `Chosen plan: ${plan}`} </output></form>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.
---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>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> </> );}<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><script lang="ts"> import { Select, SelectContent, SelectItem, SelectTrigger, } from "@/components/starwind/select";
let role = $state<string | null>(null);</script>
<Select bind:value={role} onValueChange={(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 onclick={() => { role = "admin"; }} type="button"> Choose administrator</button>For a searchable text input with listbox suggestions, install Combobox instead of adding the removed search part.
API Reference
Styled Component API
These props are added or materially changed by the installed styled component. Standard HTML attributes remain available through the inherited interfaces noted below. Expand a prop to see named type definitions and framework-specific imports. Follow the Primitive and Runtime links for lower-level behavior props.
Select
Inherits div attributes. Omits `defaultValue` and `onChange`.
Contains the following additional props:
defaultOpen boolean false
- Description
- Sets the initial open state when the component is uncontrolled.
- Classification
- Primitive override
- Primitive prop
- select.Root.defaultOpen
disabled boolean false
- Description
- Disables interaction with the component.
- Classification
- Primitive override
- Primitive prop
- select.Root.disabled
required boolean false
- Description
- Marks the control as required for form validation.
- Classification
- Primitive override
- Primitive prop
- select.Root.required
Select Trigger
Inherits button attributes.
Contains the following additional props:
asChild boolean false
- Description
- Merges the component behavior and props into its child element.
- Classification
- Primitive override
- Primitive prop
- select.Trigger.asChild
iconClass string —
- Description
- Adds classes to the component's generated icon.
- Classification
- Wrapper prop
placeholder string —
- Description
- Provides fallback text for the value element generated inside the trigger.
- Classification
- Wrapper prop
showIcon boolean true
- Description
- Shows the component's generated icon.
- Classification
- Wrapper prop
size "sm" | "md" | "lg" "md"
- Description
- Selects the component's visual size.
- Classification
- Styled variant
valueClass string —
- Description
- Adds classes to the generated value element.
- Classification
- Wrapper prop
Select Value
Inherits span attributes.
Contains the following additional props:
placeholder string —
- Description
- Provides fallback text when no value is available.
- Classification
- Wrapper prop
Select Content
Inherits div attributes.
Contains the following additional props:
disablePortal boolean false
- Description
- Keeps the public Portal wrapper inline instead of moving it to a target.
- Classification
- Wrapper prop
portalContainer string —
- Description
- Sets the CSS selector for the public Portal wrapper target.
- Classification
- Wrapper prop
size "sm" | "md" | "lg" "md"
- Description
- Selects the component's visual size.
- Classification
- Wrapper prop
Select Item
Inherits div attributes. Omits `role`.
Contains the following additional props:
disabled boolean false
- Description
- Disables interaction with the component.
- Classification
- Styled variant
- Primitive prop
- select.Item.disabled
indicatorClass string —
- Description
- Adds classes to the generated selection indicator.
- Classification
- Wrapper prop
inset boolean false
- Description
- Adds leading inset spacing for visual alignment.
- Classification
- Styled variant
showIndicator boolean true
- Description
- Shows the generated selection indicator.
- Classification
- Wrapper prop
value Required string —
- Description
- Controls or identifies the component value.
- Classification
- Primitive override
- Primitive prop
- select.Item.value
Primitive And Runtime API
Use these references when you need the lower-level behavior APIs behind Select.
Primitive API
Runtime API
- Select primitive
createSelectfrom@starwind-ui/runtime/select
Changelog
View version history v2.1.3 7 releases
v2.1.3
- A
requiredSelect now stops a plain HTML form from submitting when no option is selected. The browser shows its message next to the Select and puts focus on the Select button. Before, the empty form submitted. - A Select without
requiredstill lets the Enter key submit the form as before. - When a required Select is the first field with an error in a Starwind Form, submit and
validate({ focus: true })now put focus on the Select button, and the page scrolls to it.
v2.1.2
- Select labels in Vue and Svelte now update when the selected option’s text changes or its option loads after the value is set, whether the popup is open or closed.
- Vue Select keeps its previous selection and open state when a change is canceled.
- Reduced background work when a React page uses multiple menus, tooltips, or other portals.
- Vue Select Trigger now accepts
asChildso you can use your own element as the trigger. - React Select keeps its current selection when a reset is canceled and preserves newer values while a reset is pending. Keep it in the same form until that reset finishes.
- Fixed nested Svelte overlays inside a dialog appearing behind their parent or failing to receive clicks.
- Fixed nested overlays appearing behind their parent in Vue and Svelte. Overlays can now render under the document body; if custom CSS or DOM queries require them to stay inside a parent, set a portal container or add
data-floating-rootto that parent. - Reduced the JavaScript loaded by Select when it is used without Field.
- Fixed React 18 retaining removed popup elements when the parent component stays mounted.
v2.1.1
- Connected group headings to their groups with
aria-labelledbyso assistive technology can identify each group.
v2.1.0
- Added
portalContaineranddisablePortaltoSelectContentfor 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
SelectTriggerandSelectContentnow expose their independently resolved size asdata-size, including themddefault. 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 for the underlying unstyled anatomy and behavior API.