Forms

Native Select.

A styled native select element with the Input look, a chevron, options and option groups.

Installation#

With the CLI

$npx lanterncn add native-select

This also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/native-select. See Installation if your project is not set up yet.

Manually

  1. Install the dependencies

    npm install lucide-react
  2. Copy the source into your project

    components/ui/native-select.tsx
    import * as React from "react";
    import { ChevronDownIcon } from "lucide-react";
    
    import { cn } from "@/lib/utils";
    
    function NativeSelect({
      className,
      size = "default",
      ...props
    }: Omit<React.ComponentProps<"select">, "size"> & { size?: "sm" | "default" }) {
      return (
        <div
          data-slot="native-select-wrapper"
          className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
        >
          <select
            data-slot="native-select"
            data-size={size}
            className={cn(
              "h-10 w-full min-w-0 cursor-pointer appearance-none rounded-md border border-input bg-background/40 px-3 py-2 pr-9 text-sm text-foreground transition-[border-color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground",
              "data-[size=sm]:h-8 data-[size=sm]:py-1 data-[size=sm]:text-xs",
              "hover:border-muted-foreground/60 focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-ring/25",
              "aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:pointer-events-none disabled:cursor-not-allowed",
              className,
            )}
            {...props}
          />
          <ChevronDownIcon
            data-slot="native-select-icon"
            aria-hidden="true"
            className="pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground opacity-80 select-none group-has-[select:focus-visible]/native-select:text-primary group-has-[select[aria-invalid=true]]/native-select:text-destructive"
          />
        </div>
      );
    }
    
    function NativeSelectOption({ className, ...props }: React.ComponentProps<"option">) {
      return <option data-slot="native-select-option" className={cn("bg-popover text-popover-foreground", className)} {...props} />;
    }
    
    function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<"optgroup">) {
      return (
        <optgroup
          data-slot="native-select-optgroup"
          className={cn("bg-popover text-popover-foreground", className)}
          {...props}
        />
      );
    }
    
    export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
  3. Update the import paths to match your project

    The source imports cn from @/lib/utils.

Usage#

import { NativeSelect, NativeSelectOptGroup, NativeSelectOption } from "@/components/ui/native-select";
<NativeSelect defaultValue="overworld">
  <NativeSelectOption value="overworld">Overworld</NativeSelectOption>
  <NativeSelectOption value="deepstone">Deepstone</NativeSelectOption>
</NativeSelect>

Use Native Select when you want the platform picker, especially on phones, or need the value to work in a plain form post. Use Select for a fully styled dropdown with icons.

The wrapper is w-fit by default. Pass className="w-full" to stretch the select; the open list is drawn by the browser.

Examples#

Groups

Invalid, disabled and small

Choose a region before publishing.