shadcn.io is not affiliated with official shadcn/ui
Shadcn Native Select for React and Tailwind
Styled native HTML select element.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/native-select.jsonpnpm dlx shadcn@latest add https://kit.dev/r/native-select.jsonnpx shadcn@latest add https://kit.dev/r/native-select.jsonyarn shadcn@latest add https://kit.dev/r/native-select.jsonThis component depends on Field. Install it first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react lucide-react tailwind-variantspnpm add @ark-ui/react lucide-react tailwind-variantsnpm install @ark-ui/react lucide-react tailwind-variantsyarn add @ark-ui/react lucide-react tailwind-variantsCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import { Field as ArkField } from "@ark-ui/react/field";
import { ChevronsUpDownIcon } from "lucide-react";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
export const nativeSelectVariants = tv({
base: [
"appearance-none",
"w-full min-w-0",
"ps-2.5 pe-8",
"select-none text-sm",
"bg-transparent dark:bg-input/30",
"rounded-lg border border-input shadow-xs/5",
"transition-colors",
"outline-none",
"[&:has(option[value='']:checked)]:text-muted-foreground/64",
"disabled:pointer-events-none disabled:cursor-not-allowed",
"focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32",
"aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/24",
"dark:aria-invalid:border-destructive-foreground dark:aria-invalid:text-destructive-foreground dark:aria-invalid:ring-destructive-foreground/20",
"motion-reduce:transition-none!",
],
variants: {
size: {
sm: ["h-7"],
md: ["h-8"],
lg: ["h-9"],
},
},
defaultVariants: {
size: "md",
},
});
interface NativeSelectProps
extends Omit<React.ComponentProps<typeof ArkField.Select>, "size">,
VariantProps<typeof nativeSelectVariants> {
/**
* Whether the select is invalid.
*
* @default false
*/
invalid?: boolean;
}
export const NativeSelect = (props: NativeSelectProps) => {
const { size = "md", invalid, className, ...rest } = props;
return (
<ark.div
className={cn(
"relative w-fit",
"has-[select:disabled]:opacity-64",
"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground",
className
)}
data-slot="native-select-wrapper"
>
<ArkField.Select
aria-invalid={invalid}
className={cn(nativeSelectVariants({ size }))}
data-slot="native-select"
{...rest}
/>
<ChevronsUpDownIcon
aria-hidden="true"
className={cn("absolute inset-e-2.5 top-1/2 -translate-y-1/2")}
data-slot="native-select-icon"
/>
</ark.div>
);
};
export const NativeSelectOption = (
props: React.ComponentProps<typeof ark.option>
) => <ark.option data-slot="native-select-option" {...props} />;
export const NativeSelectOptGroup = (
props: React.ComponentProps<typeof ark.optgroup>
) => <ark.optgroup data-slot="native-select-optgroup" {...props} />;Update the import paths to match your project setup.
Usage
import {
NativeSelect,
NativeSelectOption,
} from "@/components/ui/native-select";<NativeSelect className="w-full max-w-48">
<NativeSelectOption value="">Select an option</NativeSelectOption>
<NativeSelectOption value="banana">Banana</NativeSelectOption>
<NativeSelectOption value="apple">Apple</NativeSelectOption>
<NativeSelectOption value="orange">Orange</NativeSelectOption>
<NativeSelectOption value="pineapple">Pineapple</NativeSelectOption>
</NativeSelect>Demos
Controlled
Disabled
Groups
Invalid
Size Lg
Size Md
Size Sm
With Field
API Reference
NativeSelect
div
PropType
AttributeType
NativeSelectOption
option
PropType
AttributeType
NativeSelectOptGroup
optgroup
PropType
AttributeType