Zaidan

Command Palette

Search for a command to run...

GitHub

Select

Displays a list of options for the user to pick from—triggered by a button.

Installation

CLI

Manual

Copy and paste the following code into your project.

import type { PolymorphicProps } from "@kobalte/core/polymorphic";
import * as SelectPrimitive from "@kobalte/core/select";
import {
Root,
Section,
type SelectContentProps as SelectPrimitiveContentProps,
type SelectTriggerProps as SelectPrimitiveTriggerProps,
type SelectValueProps as SelectPrimitiveValueProps,
type SelectRootProps,
type SelectSectionProps,
useSelectContext,
Value,
} from "@kobalte/core/select";
import { Check, ChevronsUpDown } from "lucide-solid";
import type { ComponentProps, JSX, ValidComponent } from "solid-js";
import { mergeProps, splitProps } from "solid-js";
import { cn } from "~/lib/utils";
type SelectProps<O, OptGroup = never, T extends ValidComponent = "div"> = PolymorphicProps<
T,
SelectRootProps<O, OptGroup, T>
> &
Pick<ComponentProps<T>, "class" | "children">;
const Select = <O, OptGroup = never, T extends ValidComponent = "div">(
props: SelectProps<O, OptGroup, T>,
) => {
const mergedProps = mergeProps(
{
sameWidth: true,
gutter: 4,
placement: "bottom",
} as const,
props,
);
return <Root {...mergedProps} />;
};
type SelectGroupProps<T extends ValidComponent = "div"> = PolymorphicProps<
T,
SelectSectionProps<T>
> &
Pick<ComponentProps<T>, "class">;
const SelectGroup = <T extends ValidComponent = "div">(props: SelectGroupProps<T>) => {
const [local, others] = splitProps(props as SelectGroupProps, ["class"]);
return <Section class={cn("z-select-group", local.class)} data-slot="select-group" {...others} />;
};
type SelectValueProps<Option, T extends ValidComponent = "span"> = PolymorphicProps<
T,
SelectPrimitiveValueProps<Option, T>
> &
Pick<ComponentProps<T>, "class">;
const SelectValue = <Option, T extends ValidComponent = "span">(
props: SelectValueProps<Option, T>,
) => {
const context = useSelectContext();
const [local, others] = splitProps(props as SelectValueProps<Option>, ["class"]);
return (
<Value
class={cn("z-select-value", local.class, {
"text-muted-foreground": context.selectedOptions().length === 0,
})}
data-slot="select-value"
{...others}
/>
);
};
type SelectTriggerProps<T extends ValidComponent = "button"> = PolymorphicProps<
T,
SelectPrimitiveTriggerProps<T>
> &
Pick<ComponentProps<T>, "class" | "children"> & {
size?: "sm" | "default";
};
const SelectTrigger = <T extends ValidComponent = "button">(rawProps: SelectTriggerProps<T>) => {
const props = mergeProps({ size: "default" }, rawProps);
const [local, others] = splitProps(props as SelectTriggerProps, ["class", "children", "size"]);
return (
<SelectPrimitive.Trigger
class={cn(
"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",
local.class,
)}
data-size={local.size}
data-slot="select-trigger"
{...others}
>
{local.children}
<SelectPrimitive.Icon as={ChevronsUpDown} class="pointer-events-none z-select-trigger-icon" />
</SelectPrimitive.Trigger>
);
};
type SelectContentProps<T extends ValidComponent = "div"> = PolymorphicProps<
T,
SelectPrimitiveContentProps<T>
> &
Pick<ComponentProps<T>, "class"> & {};
const SelectContent = <T extends ValidComponent = "div">(props: SelectContentProps<T>) => {
const [local, others] = splitProps(props as SelectContentProps, ["class"]);
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
class={cn(
"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",
local.class,
)}
data-slot="select-content"
{...others}
>
<SelectPrimitive.Listbox class="m-0 p-1" />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
);
};
type SelectLabelProps<T extends ValidComponent = "span"> = SelectPrimitive.SelectLabelProps<T> & {
class?: string | undefined;
};
const SelectLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, SelectLabelProps<T>>,
) => {
const [local, others] = splitProps(props as SelectLabelProps, ["class"]);
return (
<SelectPrimitive.Label
class={cn("z-select-label", local.class)}
data-slot="select-label"
{...others}
/>
);
};
type SelectItemProps<T extends ValidComponent = "li"> = SelectPrimitive.SelectItemProps<T> & {
class?: string | undefined;
children?: JSX.Element;
};
const SelectItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, SelectItemProps<T>>,
) => {
const [local, others] = splitProps(props as SelectItemProps, ["class", "children"]);
return (
<SelectPrimitive.Item
class={cn(
"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",
local.class,
)}
data-slot="select-item"
{...others}
>
<SelectPrimitive.ItemLabel class="z-select-item-text shrink-0 whitespace-nowrap">
{local.children}
</SelectPrimitive.ItemLabel>
<SelectPrimitive.ItemIndicator as="span" class="z-select-item-indicator">
<Check class="pointer-events-none z-select-item-indicator-icon" />
</SelectPrimitive.ItemIndicator>
</SelectPrimitive.Item>
);
};
type SelectSeparatorProps<T extends ValidComponent = "hr"> = ComponentProps<T> & {
class?: string | undefined;
};
const SelectSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, SelectSeparatorProps<T>>,
) => {
const [local, others] = splitProps(props as SelectSeparatorProps, ["class"]);
return (
<hr
class={cn("pointer-events-none z-select-separator", local.class)}
data-slot="select-separator"
{...others}
/>
);
};
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue,
};

Props

The Select component uses Kobalte's Select primitive under the hood. Here are the main props:

PropTypeDefaultDescription
optionsT[]-The array of options to display
optionValuekeyof T | ((option: T) => string)-Key or function to get the option's value
optionTextValuekeyof T | ((option: T) => string)-Key or function to get the option's text
optionDisabledkeyof T | ((option: T) => boolean)-Key or function to determine if option is disabled
valueT-The controlled value of the select
defaultValueT-The default value of the select
onChange(value: T) => void-Handler called when value changes
placeholderstring-Placeholder text when no value is selected
disabledbooleanfalseWhether the select is disabled
validationState"valid" | "invalid"-The validation state of the select
multiplebooleanfalseWhether multiple options can be selected

SelectTrigger

PropTypeDefaultDescription
size"sm" | "default""default"The size variant of the trigger
classstring-Additional CSS classes

SelectContent

The SelectContent component wraps the dropdown portal and listbox. It inherits positioning from the Select root.

SelectItem

PropTypeDefaultDescription
itemListState.Option<T>-The item object from Kobalte
disabledbooleanfalseWhether the item is disabled

Examples

Here are the source code of all the examples from the preview page:

Basic

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>
);
}

With Icons

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>
);
}

With Groups & Labels

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>
);
}

Large List

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>
);
}

Multiple Selection

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>
);
}

Sizes

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>
);
}

With Button

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>
);
}

Item Aligned

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>
);
}

With Field

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>
);
}

Invalid

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>
);
}

Inline with Input & NativeSelect

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>
);
}

Disabled

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>
);
}

Subscription Plan

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>
);
}

In Dialog

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>
);
}