Search for a command to run...
Displays a list of options for the user to pick from—triggered by a button.
npx shadcn@latest add @zaidan/selectpnpx shadcn add @zaidan/selectyarn dlx shadcn@latest add @zaidan/selectbunx shadcn@latest add @zaidan/selectCopy and paste the following code into your project.
1import type { PolymorphicProps } from "@kobalte/core/polymorphic";2import * as SelectPrimitive from "@kobalte/core/select";3import {4 Root,5 Section,6 type SelectContentProps as SelectPrimitiveContentProps,7 type SelectTriggerProps as SelectPrimitiveTriggerProps,8 type SelectValueProps as SelectPrimitiveValueProps,9 type SelectRootProps,10 type SelectSectionProps,11 useSelectContext,12 Value,13} from "@kobalte/core/select";14import { Check, ChevronsUpDown } from "lucide-solid";15import type { ComponentProps, JSX, ValidComponent } from "solid-js";16import { mergeProps, splitProps } from "solid-js";17import { cn } from "~/lib/utils";18
19type SelectProps<O, OptGroup = never, T extends ValidComponent = "div"> = PolymorphicProps<20 T,21 SelectRootProps<O, OptGroup, T>22> &23 Pick<ComponentProps<T>, "class" | "children">;24
25const Select = <O, OptGroup = never, T extends ValidComponent = "div">(26 props: SelectProps<O, OptGroup, T>,27) => {28 const mergedProps = mergeProps(29 {30 sameWidth: true,31 gutter: 4,32 placement: "bottom",33 } as const,34 props,35 );36 return <Root {...mergedProps} />;37};38
39type SelectGroupProps<T extends ValidComponent = "div"> = PolymorphicProps<40 T,41 SelectSectionProps<T>42> &43 Pick<ComponentProps<T>, "class">;44
45const SelectGroup = <T extends ValidComponent = "div">(props: SelectGroupProps<T>) => {46 const [local, others] = splitProps(props as SelectGroupProps, ["class"]);47 return <Section class={cn("z-select-group", local.class)} data-slot="select-group" {...others} />;48};49
50type SelectValueProps<Option, T extends ValidComponent = "span"> = PolymorphicProps<51 T,52 SelectPrimitiveValueProps<Option, T>53> &54 Pick<ComponentProps<T>, "class">;55
56const SelectValue = <Option, T extends ValidComponent = "span">(57 props: SelectValueProps<Option, T>,58) => {59 const context = useSelectContext();60 const [local, others] = splitProps(props as SelectValueProps<Option>, ["class"]);61 return (62 <Value63 class={cn("z-select-value", local.class, {64 "text-muted-foreground": context.selectedOptions().length === 0,65 })}66 data-slot="select-value"67 {...others}68 />69 );70};71
72type SelectTriggerProps<T extends ValidComponent = "button"> = PolymorphicProps<73 T,74 SelectPrimitiveTriggerProps<T>75> &76 Pick<ComponentProps<T>, "class" | "children"> & {77 size?: "sm" | "default";78 };79
80const SelectTrigger = <T extends ValidComponent = "button">(rawProps: SelectTriggerProps<T>) => {81 const props = mergeProps({ size: "default" }, rawProps);82 const [local, others] = splitProps(props as SelectTriggerProps, ["class", "children", "size"]);83
84 return (85 <SelectPrimitive.Trigger86 class={cn(87 "z-select-trigger flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0",88 local.class,89 )}90 data-size={local.size}91 data-slot="select-trigger"92 {...others}93 >94 {local.children}95 <SelectPrimitive.Icon as={ChevronsUpDown} class="pointer-events-none z-select-trigger-icon" />96 </SelectPrimitive.Trigger>97 );98};99
100type SelectContentProps<T extends ValidComponent = "div"> = PolymorphicProps<101 T,102 SelectPrimitiveContentProps<T>103> &104 Pick<ComponentProps<T>, "class"> & {};105
106const SelectContent = <T extends ValidComponent = "div">(props: SelectContentProps<T>) => {107 const [local, others] = splitProps(props as SelectContentProps, ["class"]);108 return (109 <SelectPrimitive.Portal>110 <SelectPrimitive.Content111 class={cn(112 "relative isolate z-50 z-menu-target z-select-content max-h-(--kb-popper-available-height) min-w-32 origin-(--kb-select-content-transform-origin) overflow-y-auto overflow-x-hidden",113 local.class,114 )}115 data-slot="select-content"116 {...others}117 >118 <SelectPrimitive.Listbox class="m-0 p-1" />119 </SelectPrimitive.Content>120 </SelectPrimitive.Portal>121 );122};123
124type SelectLabelProps<T extends ValidComponent = "span"> = SelectPrimitive.SelectLabelProps<T> & {125 class?: string | undefined;126};127
128const SelectLabel = <T extends ValidComponent = "span">(129 props: PolymorphicProps<T, SelectLabelProps<T>>,130) => {131 const [local, others] = splitProps(props as SelectLabelProps, ["class"]);132 return (133 <SelectPrimitive.Label134 class={cn("z-select-label", local.class)}135 data-slot="select-label"136 {...others}137 />138 );139};140
141type SelectItemProps<T extends ValidComponent = "li"> = SelectPrimitive.SelectItemProps<T> & {142 class?: string | undefined;143 children?: JSX.Element;144};145
146const SelectItem = <T extends ValidComponent = "li">(147 props: PolymorphicProps<T, SelectItemProps<T>>,148) => {149 const [local, others] = splitProps(props as SelectItemProps, ["class", "children"]);150 return (151 <SelectPrimitive.Item152 class={cn(153 "relative z-select-item flex w-full cursor-default select-none items-center outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",154 local.class,155 )}156 data-slot="select-item"157 {...others}158 >159 <SelectPrimitive.ItemLabel class="z-select-item-text shrink-0 whitespace-nowrap">160 {local.children}161 </SelectPrimitive.ItemLabel>162 <SelectPrimitive.ItemIndicator as="span" class="z-select-item-indicator">163 <Check class="pointer-events-none z-select-item-indicator-icon" />164 </SelectPrimitive.ItemIndicator>165 </SelectPrimitive.Item>166 );167};168
169type SelectSeparatorProps<T extends ValidComponent = "hr"> = ComponentProps<T> & {170 class?: string | undefined;171};172
173const SelectSeparator = <T extends ValidComponent = "hr">(174 props: PolymorphicProps<T, SelectSeparatorProps<T>>,175) => {176 const [local, others] = splitProps(props as SelectSeparatorProps, ["class"]);177 return (178 <hr179 class={cn("pointer-events-none z-select-separator", local.class)}180 data-slot="select-separator"181 {...others}182 />183 );184};185
186export {187 Select,188 SelectContent,189 SelectGroup,190 SelectItem,191 SelectLabel,192 SelectSeparator,193 SelectTrigger,194 SelectValue,195};The Select component uses Kobalte's Select primitive under the hood. Here are the main props:
| Prop | Type | Default | Description |
|---|---|---|---|
options | T[] | - | The array of options to display |
optionValue | keyof T | ((option: T) => string) | - | Key or function to get the option's value |
optionTextValue | keyof T | ((option: T) => string) | - | Key or function to get the option's text |
optionDisabled | keyof T | ((option: T) => boolean) | - | Key or function to determine if option is disabled |
value | T | - | The controlled value of the select |
defaultValue | T | - | The default value of the select |
onChange | (value: T) => void | - | Handler called when value changes |
placeholder | string | - | Placeholder text when no value is selected |
disabled | boolean | false | Whether the select is disabled |
validationState | "valid" | "invalid" | - | The validation state of the select |
multiple | boolean | false | Whether multiple options can be selected |
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "default" | The size variant of the trigger |
class | string | - | Additional CSS classes |
The SelectContent component wraps the dropdown portal and listbox. It inherits positioning from the Select root.
| Prop | Type | Default | Description |
|---|---|---|---|
item | ListState.Option<T> | - | The item object from Kobalte |
disabled | boolean | false | Whether the item is disabled |
Here are the source code of all the examples from the preview page:
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectBasic() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes", disabled: true }, { label: "Pineapple", value: "pineapple" }, ];
return ( <Example title="Basic"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item} data-disabled={props.item.rawValue.disabled}> {props.item.rawValue.label} </SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { ChartBar, ChartLine, ChartPie } from "lucide-solid";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectWithIcons() { const getLabel = (item: string) => { return match(item) .with("line", () => ( <> <ChartLine /> Line </> )) .with("bar", () => ( <> <ChartBar /> Bar </> )) .otherwise(() => ( <> <ChartPie /> Pie </> )); };
return ( <Example title="With Icons"> <div class="flex flex-col gap-4"> <Select options={["line", "bar", "pie"]} placeholder={ <> <ChartLine /> Chart Type </> } itemComponent={(props) => ( <SelectItem item={props.item}>{getLabel(props.item.rawValue)}</SelectItem> )} > <SelectTrigger size="sm"> <SelectValue<string>>{(state) => getLabel(state.selectedOption())}</SelectValue> </SelectTrigger> <SelectContent /> </Select> <Select options={["line", "bar", "pie"]} placeholder={ <> <ChartLine /> Chart Type </> } itemComponent={(props) => ( <SelectItem item={props.item}>{getLabel(props.item.rawValue)}</SelectItem> )} > <SelectTrigger size="default"> <SelectValue<string>>{(state) => getLabel(state.selectedOption())}</SelectValue> </SelectTrigger> <SelectContent /> </Select> </div> </Example> );}import { Show } from "solid-js";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectWithGroups() { type FoodOption = { label: string; value: string; };
type Food = { label: string; options: FoodOption[]; };
const foods: Food[] = [ { label: "Fruits", options: [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ], }, { label: "Vegetables", options: [ { label: "Carrot", value: "carrot" }, { label: "Broccoli", value: "broccoli" }, { label: "Spinach", value: "spinach" }, ], }, ];
return ( <Example title="With Groups & Labels"> <Select<FoodOption, Food> options={foods} optionValue="value" optionTextValue="label" optionGroupChildren="options" placeholder="Select a food" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} sectionComponent={(props) => ( <> <Show when={props.section.index !== 0}> <SelectSeparator /> </Show> <SelectGroup> <SelectLabel>{props.section.rawValue.label}</SelectLabel> </SelectGroup> </> )} > <SelectTrigger> <SelectValue<FoodOption>>{(state) => state.selectedOption().label}</SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectLargeList() { const items = Array.from({ length: 100 }).map((_, i) => ({ label: `Item ${i}`, value: `item-${i}`, }));
return ( <Example title="Large List"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select an item" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectMultiple() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" }, { label: "Strawberry", value: "strawberry" }, { label: "Watermelon", value: "watermelon" }, ];
return ( <Example title="Multiple Selection"> <Select<(typeof items)[number]> options={items} optionValue="value" optionTextValue="label" placeholder="Select fruits" multiple defaultValue={[]} itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger class="w-72"> <SelectValue<(typeof items)[number]>> {(state) => { if (state.selectedOptions().length === 1) { return state.selectedOptions()[0].label; } return `${state.selectedOptions().length} fruits selected`; }} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectSizes() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ]; return ( <Example title="Sizes"> <div class="flex flex-col gap-4"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Small" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger size="sm"> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Default" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger size="default"> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </div> </Example> );}import { Button } from "~/components/ui/button";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectWithButton() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, ]; return ( <Example title="With Button"> <div class="flex flex-col gap-4"> <div class="flex items-center gap-2"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger size="sm"> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <Button variant="outline" size="sm"> Submit </Button> </div> <div class="flex items-center gap-2"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <Button variant="outline">Submit</Button> </div> </div> </Example> );}import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectItemAligned() { const items = [ { label: "Apple", value: "apple", disabled: false }, { label: "Banana", value: "banana", disabled: false }, { label: "Blueberry", value: "blueberry", disabled: false }, { label: "Grapes", value: "grapes", disabled: true }, { label: "Pineapple", value: "pineapple", disabled: false }, ]; return ( <Example title="Item Aligned"> <Select options={items} optionValue="value" optionTextValue="label" optionDisabled="disabled" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { Field, FieldDescription, FieldLabel } from "~/components/ui/field";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectWithField() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" }, ]; return ( <Example title="With Field"> <Field> <FieldLabel for="select-fruit">Favorite Fruit</FieldLabel> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger id="select-fruit"> <SelectValue /> </SelectTrigger> <SelectContent /> </Select> <FieldDescription>Choose your favorite fruit from the list.</FieldDescription> </Field> </Example> );}import { Field, FieldError, FieldLabel } from "~/components/ui/field";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectInvalid() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" }, ]; return ( <Example title="Invalid"> <div class="flex flex-col gap-4"> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" validationState="invalid" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger aria-invalid="true"> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <Field data-invalid> <FieldLabel for="select-fruit-invalid">Favorite Fruit</FieldLabel> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" validationState="invalid" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger id="select-fruit-invalid" aria-invalid> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <FieldError errors={[{ message: "Please select a valid fruit." }]} /> </Field> </div> </Example> );}import { Input } from "~/components/ui/input";import { NativeSelect, NativeSelectOption } from "~/components/ui/native-select";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectInline() { const items = [ { label: "All", value: "all" }, { label: "Active", value: "active" }, { label: "Inactive", value: "inactive" }, ]; return ( <Example title="Inline with Input & NativeSelect"> <div class="flex items-center gap-2"> <Input placeholder="Search..." class="flex-1" /> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Filter" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger class="w-[140px]"> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> <NativeSelect class="w-[140px]"> <NativeSelectOption value="">Sort by</NativeSelectOption> <NativeSelectOption value="name">Name</NativeSelectOption> <NativeSelectOption value="date">Date</NativeSelectOption> <NativeSelectOption value="status">Status</NativeSelectOption> </NativeSelect> </div> </Example> );}import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectDisabled() { const items = [ { label: "Apple", value: "apple", disabled: false }, { label: "Banana", value: "banana", disabled: false }, { label: "Blueberry", value: "blueberry", disabled: false }, { label: "Grapes", value: "grapes", disabled: true }, { label: "Pineapple", value: "pineapple", disabled: false }, ]; return ( <Example title="Disabled"> <Select options={items} optionValue="value" optionTextValue="label" optionDisabled="disabled" placeholder="Select a fruit" disabled itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}import { Item, ItemContent, ItemDescription, ItemTitle } from "~/components/ui/item";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectPlan() { return ( <Example title="Subscription Plan"> <Select options={plans} optionValue="name" optionTextValue="name" defaultValue={plans[0]} itemComponent={(props) => ( <SelectItem item={props.item}> <SelectPlanItem plan={props.item.rawValue} /> </SelectItem> )} > <SelectTrigger class="h-auto! w-72"> <SelectValue<(typeof plans)[number]>> {(state) => <SelectPlanItem plan={state.selectedOption()} />} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </Example> );}
function SelectPlanItem(props: { plan: (typeof plans)[number] }) { return ( <Item size="xs" class="w-full p-0"> <ItemContent class="gap-0"> <ItemTitle>{props.plan.name}</ItemTitle> <ItemDescription class="text-xs">{props.plan.description}</ItemDescription> </ItemContent> </Item> );}import { Button } from "~/components/ui/button";import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger,} from "~/components/ui/dialog";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "~/components/ui/select";function SelectInDialog() { const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Blueberry", value: "blueberry" }, { label: "Grapes", value: "grapes" }, { label: "Pineapple", value: "pineapple" }, ]; return ( <Example title="In Dialog"> <Dialog> <DialogTrigger as={Button} variant="outline"> Open Dialog </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Select Example</DialogTitle> <DialogDescription>Use the select below to choose a fruit.</DialogDescription> </DialogHeader> <Select options={items} optionValue="value" optionTextValue="label" placeholder="Select a fruit" itemComponent={(props) => ( <SelectItem item={props.item}>{props.item.rawValue.label}</SelectItem> )} > <SelectTrigger> <SelectValue<(typeof items)[number]>> {(state) => state.selectedOption().label} </SelectValue> </SelectTrigger> <SelectContent /> </Select> </DialogContent> </Dialog> </Example> );}