Select
Accessible dropdown selector. Supports groups, disabled options, and custom triggers.
Examples
Align Item With Trigger
Use the position prop on SelectContent to control alignment. When position='item-aligned' (default), the popup positions so the selected item appears over the trigger. When position='popper', the popup aligns to the trigger edge.
Toggle to align the item with the trigger.
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
import { Switch } from "@codefast/ui/switch";
import * as React from "react";
export function SelectAlignItem() {
const [alignItemWithTrigger, setAlignItemWithTrigger] = React.useState(true);
return (
<FieldGroup className="w-full max-w-xs">
<Field orientation="horizontal">
<FieldContent>
<FieldLabel htmlFor="align-item">Align Item</FieldLabel>
<FieldDescription>Toggle to align the item with the trigger.</FieldDescription>
</FieldContent>
<Switch id="align-item" checked={alignItemWithTrigger} onCheckedChange={setAlignItemWithTrigger} />
</Field>
<Field>
<Select defaultValue="banana">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent position={alignItemWithTrigger ? "item-aligned" : "popper"}>
<SelectGroup>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes">Grapes</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</Field>
</FieldGroup>
);
}
Disabled
Displays a list of options for the user to pick from—triggered by a button.
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
export function SelectDisabled() {
return (
<Select disabled>
<SelectTrigger className="w-full max-w-48">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes" disabled>
Grapes
</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}
Groups
Use SelectGroup, SelectLabel, and SelectSeparator to organize items.
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@codefast/ui/select";
export function SelectGroups() {
return (
<Select>
<SelectTrigger className="w-full max-w-48">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>Vegetables</SelectLabel>
<SelectItem value="carrot">Carrot</SelectItem>
<SelectItem value="broccoli">Broccoli</SelectItem>
<SelectItem value="spinach">Spinach</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}
Invalid
Set data-invalid on the Field and aria-invalid on the SelectTrigger to show an error state.
import { Field, FieldError, FieldLabel } from "@codefast/ui/field";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
export function SelectInvalid() {
return (
<Field data-invalid className="w-full max-w-48">
<FieldLabel>Fruit</FieldLabel>
<Select>
<SelectTrigger aria-invalid>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FieldError>Please select a fruit.</FieldError>
</Field>
);
}
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@codefast/ui/select";
import * as React from "react";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
const translations: Translations = {
en: {
dir: "ltr",
values: {
selectFruit: "Select a fruit",
fruits: "Fruits",
apple: "Apple",
banana: "Banana",
blueberry: "Blueberry",
grapes: "Grapes",
pineapple: "Pineapple",
vegetables: "Vegetables",
carrot: "Carrot",
broccoli: "Broccoli",
spinach: "Spinach",
},
},
ar: {
dir: "rtl",
values: {
selectFruit: "اختر فاكهة",
fruits: "الفواكه",
apple: "تفاح",
banana: "موز",
blueberry: "توت أزرق",
grapes: "عنب",
pineapple: "أناناس",
vegetables: "الخضروات",
carrot: "جزر",
broccoli: "بروكلي",
spinach: "سبانخ",
},
},
he: {
dir: "rtl",
values: {
selectFruit: "בחר פרי",
fruits: "פירות",
apple: "תפוח",
banana: "בננה",
blueberry: "אוכמניה",
grapes: "ענבים",
pineapple: "אננס",
vegetables: "ירקות",
carrot: "גזר",
broccoli: "ברוקולי",
spinach: "תרד",
},
},
};
export function SelectRtl() {
const { dir, t, language } = useTranslation(translations, "ar");
const [selectedFruit, setSelectedFruit] = React.useState<string>("");
const fruits = [
{ label: t.apple, value: "apple" },
{ label: t.banana, value: "banana" },
{ label: t.blueberry, value: "blueberry" },
{ label: t.grapes, value: "grapes" },
{ label: t.pineapple, value: "pineapple" },
];
const vegetables = [
{ label: t.carrot, value: "carrot" },
{ label: t.broccoli, value: "broccoli" },
{ label: t.spinach, value: "spinach" },
];
return (
<Select value={selectedFruit} onValueChange={setSelectedFruit}>
<SelectTrigger className="w-32" dir={dir}>
<SelectValue placeholder={t.selectFruit} />
</SelectTrigger>
<SelectContent dir={dir} data-lang={dir === "rtl" ? language : undefined}>
<SelectGroup>
<SelectLabel>{t.fruits}</SelectLabel>
{fruits.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
<SelectSeparator />
<SelectGroup>
<SelectLabel>{t.vegetables}</SelectLabel>
{vegetables.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
}
Scrollable
A select with many items that scrolls.
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@codefast/ui/select";
export function SelectScrollable() {
return (
<Select>
<SelectTrigger className="w-full max-w-64">
<SelectValue placeholder="Select a timezone" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>North America</SelectLabel>
<SelectItem value="est">Eastern Standard Time</SelectItem>
<SelectItem value="cst">Central Standard Time</SelectItem>
<SelectItem value="mst">Mountain Standard Time</SelectItem>
<SelectItem value="pst">Pacific Standard Time</SelectItem>
<SelectItem value="akst">Alaska Standard Time</SelectItem>
<SelectItem value="hst">Hawaii Standard Time</SelectItem>
</SelectGroup>
<SelectGroup>
<SelectLabel>Europe & Africa</SelectLabel>
<SelectItem value="gmt">Greenwich Mean Time</SelectItem>
<SelectItem value="cet">Central European Time</SelectItem>
<SelectItem value="eet">Eastern European Time</SelectItem>
<SelectItem value="west">Western European Summer Time</SelectItem>
<SelectItem value="cat">Central Africa Time</SelectItem>
<SelectItem value="eat">East Africa Time</SelectItem>
</SelectGroup>
<SelectGroup>
<SelectLabel>Asia</SelectLabel>
<SelectItem value="msk">Moscow Time</SelectItem>
<SelectItem value="ist">India Standard Time</SelectItem>
<SelectItem value="cst_china">China Standard Time</SelectItem>
<SelectItem value="jst">Japan Standard Time</SelectItem>
<SelectItem value="kst">Korea Standard Time</SelectItem>
<SelectItem value="ist_indonesia">Indonesia Central Standard Time</SelectItem>
</SelectGroup>
<SelectGroup>
<SelectLabel>Australia & Pacific</SelectLabel>
<SelectItem value="awst">Australian Western Standard Time</SelectItem>
<SelectItem value="acst">Australian Central Standard Time</SelectItem>
<SelectItem value="aest">Australian Eastern Standard Time</SelectItem>
<SelectItem value="nzst">New Zealand Standard Time</SelectItem>
<SelectItem value="fjt">Fiji Time</SelectItem>
</SelectGroup>
<SelectGroup>
<SelectLabel>South America</SelectLabel>
<SelectItem value="art">Argentina Time</SelectItem>
<SelectItem value="bot">Bolivia Time</SelectItem>
<SelectItem value="brt">Brasilia Time</SelectItem>
<SelectItem value="clt">Chile Standard Time</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
export function SelectUsage() {
return (
<Select>
<SelectTrigger className="w-40">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
</SelectContent>
</Select>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- ARIA listbox with type-ahead — start typing to jump to a matching option.
- position="item-aligned" (default) opens with the selected item over the trigger; position="popper" anchors to the trigger edge instead.
- Two trigger sizes (default, sm) and built-in scroll-up/down buttons for long lists.
- SelectGroup/SelectLabel/SelectSeparator organize options into announced groups.
API reference
Props for each part of the component. All native element props are also forwarded.
Select
Root. Owns the selected value.
valuestringThe controlled selected value.
defaultValuestringThe selected value when initially rendered (uncontrolled).
onValueChange(value: string) => voidCalled when the selected value changes.
disabledbooleanDisables the whole control.
Default
false
SelectGroup
Wraps related options so screen readers announce the group, not just visual spacing.
childrenReactNodeA SelectLabel followed by the group's SelectItems.
SelectLabel
Heading text for a SelectGroup.
childrenReactNodeHeading text for the group (required inside SelectGroup for accessibility).
SelectItem
valuestringThe value submitted when this item is chosen (required).
disabledbooleanMakes a single option non-selectable.
Default
false
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
| Key | Function |
|---|---|
| Space | Opens the menu when the trigger is focused. |
| Arrow+Down | Moves to the next option. |
| Arrow+Up | Moves to the previous option. |
| Enter | Selects the highlighted option. |
| Esc | Closes without changing the value. |
- Implements the ARIA listbox pattern with type-ahead — start typing to jump to an option.
- Use SelectLabel inside a SelectGroup so groups are announced, not just visually separated.
- For native mobile behaviour with zero JS, prefer Native Select.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Always set a placeholder via SelectValue for empty state.
- Group and label options once a list grows past ~7 items.
Don’t
- Don’t use Select for boolean choices — use a Switch or Checkbox.
- Don’t put interactive controls other than options inside the menu.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.