A styled native HTML select element with consistent design system integration.
1import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";23export default function Preview() {4 return (Installation
pnpm dlx sprawlify@latest add native-selectnpx sprawlify@latest add native-selectyarn sprawlify@latest add native-selectbunx --bun sprawlify@latest add native-select
Install the following dependencies:
pnpm add @sprawlify/primitives @sprawlify/solidnpm install @sprawlify/primitives @sprawlify/solidyarn add @sprawlify/primitives @sprawlify/solidbun add @sprawlify/primitives @sprawlify/solid
Add the following files to your project:
1import { cn } from "@/lib/utils";2import { sprawlify } from "@sprawlify/solid";3import { ChevronDownIcon } from "lucide-solid";4import { splitProps, type ComponentProps } from "solid-js";56type NativeSelectProps = Omit<ComponentProps<typeof sprawlify.select>, "size"> & {7 size?: "sm" | "default";8};910function NativeSelect(props: NativeSelectProps) {11 const [local, others] = splitProps(props, ["class", "size"]);12 const size = () => local.size || "default";1314 return (15 <div16 class={cn("group/native-select relative w-fit has-[select:disabled]:opacity-50", local.class)}17 data-scope="native-select"18 data-part="root"19 data-slot="native-select-root"20 data-size={size()}21 >22 <sprawlify.select23 data-slot="native-select"24 data-size={size()}25 class="border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-8 w-full min-w-0 appearance-none rounded-lg border bg-input/30 py-1 pr-8 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-[size=sm]:py-0.5 outline-none disabled:pointer-events-none disabled:cursor-not-allowed"26 {...others}27 />28 <ChevronDownIcon29 class="text-muted-foreground top-1/2 right-2.5 size-4 -translate-y-1/2 pointer-events-none absolute select-none"30 aria-hidden="true"31 data-slot="native-select-icon"32 />33 </div>34 );35}3637function NativeSelectOption(props: ComponentProps<typeof sprawlify.option>) {38 return (39 <sprawlify.option40 data-scope="native-select"41 data-part="option"42 data-slot="native-select-option"43 {...props}44 />45 );46}4748function NativeSelectOptGroup(props: ComponentProps<typeof sprawlify.optgroup>) {49 const [local, others] = splitProps(props, ["class"]);5051 return (52 <sprawlify.optgroup53 data-scope="native-select"54 data-part="optgroup"55 data-slot="native-select-optgroup"56 class={cn(local.class)}57 {...others}58 />59 );60}6162export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };63Update the import paths to match your project setup.
Usage
1import {
2 NativeSelect,
3 NativeSelectOptGroup,
4 NativeSelectOption
5} from "@/components/ui/native-select"1<NativeSelect>
2 <NativeSelectOption value="">Select a fruit</NativeSelectOption>
3 <NativeSelectOption value="apple">Apple</NativeSelectOption>
4 <NativeSelectOption value="banana">Banana</NativeSelectOption>
5 <NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
6 <NativeSelectOption value="pineapple">Pineapple</NativeSelectOption>
7</NativeSelect>Examples
Disabled
Add the disabled prop to the NativeSelect component to disable the select.
1import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";23export default function Preview() {4 return (Groups
Use NativeSelectOptGroup to organize options into categories.
1import {2 NativeSelect,3 NativeSelectOptGroup,4 NativeSelectOption,Invalid
Use aria-invalid to show validation errors and the data-invalid attribute to the Field component for styling.
1import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";23export default function Preview() {4 return (