ComponentsSelect
Select
Let users choose one option from a dropdown list.
On this page
Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.
View registry filesImport the component into your page or component file.
import { Select, SelectItem } from "@dethink/components";
export function Example() {
return (
<Select label="Region" name="region" placeholder="Choose a region">
<SelectItem value="us">United States</SelectItem>
<SelectItem value="uk">United Kingdom</SelectItem>
</Select>
);
}Try the examples, then open the code to use them in your app. Open with Space or Enter, jump options by typing, and confirm with Enter.
Basic
Label, placeholder, helper description, and a name for native form submission.
Show sourceexamples/select/basic.tsx
"use client";
import { Select, SelectItem } from "@dethink/components";
export function SelectBasic() {
return (
<div className="mx-auto max-w-xs">
<Select
label="Region"
placeholder="Choose a region"
description="Data is stored in the selected region."
name="region"
>
<SelectItem value="us-east">US East (N. Virginia)</SelectItem>
<SelectItem value="eu-west">EU West (Ireland)</SelectItem>
<SelectItem value="ap-south">AP South (Mumbai)</SelectItem>
</Select>
</div>
);
}Dynamic and rich options
Pass items to build options from data. Add textValue when an option contains icons or extra text, so users can still find it by typing.
Show sourceexamples/select/options.tsx
"use client";
import { Select, SelectItem } from "@dethink/components";
const models = [
{ value: "fast", label: "Fast", note: "Lowest latency" },
{ value: "balanced", label: "Balanced", note: "Default quality" },
{ value: "reasoning", label: "Reasoning", note: "Deep analysis" },
{ value: "legacy", label: "Legacy", note: "Deprecated" },
];
export function SelectOptions() {
return (
<div className="mx-auto max-w-xs">
<Select
label="Model"
defaultValue="balanced"
items={models}
disabledKeys={["legacy"]}
>
{(item) => (
<SelectItem
key={item.value}
value={item.value}
textValue={item.label}
>
<span className="flex w-full items-baseline justify-between gap-3">
{item.label}
<span className="text-muted-foreground text-xs">{item.note}</span>
</span>
</SelectItem>
)}
</Select>
</div>
);
}Sizes
controlSize aligns the trigger with Input and Button heights.
Show sourceexamples/select/sizes.tsx
"use client";
import { Select, SelectItem } from "@dethink/components";
const sizes = ["sm", "md", "lg"] as const;
export function SelectSizes() {
return (
<div className="mx-auto grid max-w-md gap-4">
{sizes.map((size) => (
<Select
key={size}
controlSize={size}
label={`Size ${size}`}
defaultValue="staging"
>
<SelectItem value="production">Production</SelectItem>
<SelectItem value="staging">Staging</SelectItem>
<SelectItem value="sandbox">Sandbox</SelectItem>
</Select>
))}
</div>
);
}Form states
Required, disabled, read-only, and invalid with an error message.
Show sourceexamples/select/states.tsx
"use client";
import { Select, SelectItem } from "@dethink/components";
const plans = [
{ value: "starter", label: "Starter" },
{ value: "team", label: "Team" },
{ value: "scale", label: "Scale" },
];
export function SelectStates() {
return (
<div className="mx-auto grid max-w-md gap-5 sm:grid-cols-2">
<Select label="Required" required placeholder="Pick a plan" items={plans}>
{(item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
)}
</Select>
<Select label="Disabled" disabled defaultValue="team" items={plans}>
{(item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
)}
</Select>
<Select label="Read-only" readOnly defaultValue="scale" items={plans}>
{(item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
)}
</Select>
<Select
label="Invalid"
invalid
errorMessage="Your card was declined for this plan."
defaultValue="scale"
items={plans}
>
{(item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
)}
</Select>
</div>
);
}Examples that combine components for common tasks.
Live density switcher
Let users choose how much space controls use. Changing the selection updates the surrounding layout.
Show sourceexamples/select/recipe-density.tsx
"use client";
import { useState } from "react";
import {
Button,
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
Input,
Select,
SelectItem,
} from "@dethink/components";
/**
* The library's density contract is a plain `data-density` attribute, so a
* Select can retheme an entire subtree live — no component changes needed.
*/
export function SelectRecipeDensity() {
const [density, setDensity] = useState("default");
return (
<div className="space-y-5">
<div className="mx-auto max-w-xs">
<Select
label="Interface density"
value={density}
onValueChange={(value) => setDensity(value)}
>
<SelectItem value="compact">Compact</SelectItem>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="comfortable">Comfortable</SelectItem>
</Select>
</div>
<div data-density={density}>
<Card className="mx-auto max-w-sm">
<CardHeader>
<CardTitle>Invite teammate</CardTitle>
<CardDescription>
Everything below respacing is driven by one attribute.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<div className="space-y-1.5">
<label
htmlFor="density-invite-email"
className="text-sm font-medium"
>
Email
</label>
<Input
id="density-invite-email"
placeholder="teammate@company.com"
/>
</div>
</CardContent>
<CardFooter justify="end">
<Button variant="ghost">Cancel</Button>
<Button>Send invite</Button>
</CardFooter>
</Card>
</div>
</div>
);
}Use the label prop to name the field. Select connects the label, help text, and error message for you.
| Prop | What it does | Default |
|---|---|---|
valuestring | The selected value. Use with onValueChange when your app manages the selection. | Not set |
defaultValuestring | The starting selection when the component manages its own state. | Not set |
onValueChange(value: string) => void | Called with the value the user selects. | Not set |
childrenSelectItem nodes | (item) => SelectItem | Add SelectItem elements, or a function that renders each entry in items. | Not set |
itemsIterable<SelectItemData> | A list of options. Each needs a value; use the children function to render it. | Not set |
disabledKeysIterable<string> | The values of options users cannot select. | Not set |
label / description / errorMessageReactNode | The field label, help text, and error message. | Not set |
placeholderstring | Text shown in the trigger before a selection exists. | Not set |
controlSize"sm" | "md" | "lg" | Sets the button height and text size. | "md" |
required / disabled / readOnly / invalidboolean | Makes the field required, disabled, read-only, or invalid. | false |
open / defaultOpen / onOpenChangeboolean / boolean / (open) => void | Use open with onOpenChange to manage the list, or defaultOpen to set its starting state. | Not set |
namestring | The field name used when submitting a form. | Not set |
| Prop | What it does | Default |
|---|---|---|
valuestring | A unique value for this option. Returned by onValueChange and submitted with the form. | Not set |
textValuestring | Text used to find this option when the user types. Set it when the option includes more than plain text. | text content |
disabledboolean | Disables this option only. | false |