GitHub

Select Model


Install

Install Dependencies

pnpm add @base-ui-components/react

Copy and paste the following code into your project.

import type React from "react";
import { motion } from "motion/react";
import { Select } from "@base-ui-components/react";
import { Check, ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
 
const exampleData = [
  "ChatGPT 4o",
  "ChatGPT 4o-mini",
  "ChatGPT 4-turbo",
  "ChatGPT o1",
  "ChatGPT o1-mini",
];
 
function generateLetters(value: string) {
  const letterCount = new Map<string, number>();
  return value.split("").map((letter) => {
    const count = letterCount.get(letter) || 0;
    const key = `${letter}-${count}`;
    letterCount.set(letter, count + 1);
    return { letter, key };
  });
}
 
function SelectModel() {
  return (
    <Select.Root defaultValue={exampleData[0]} alignItemToTrigger={false}>
      <Select.Trigger className="relative flex h-10 items-center justify-between gap-3 text-sm rounded-md pr-3 pl-3.5 select-none hover:bg-neutral-100 focus-visible:outline-none focus-visible:-outline-offset-1 active:bg-neutral-100 data-[popup-open]:bg-neutral-100">
        <Select.Value placeholder="Select Model">
          {(value) => (
            <>
              <span className="sr-only">{value}</span>
              {generateLetters(value).map(({ letter, key }) => (
                <motion.span
                  aria-hidden
                  key={key}
                  layoutId={key}
                  className="inline-block"
                  transition={{ type: "spring", bounce: 0.35 }}
                >
                  {letter.trim() || "\u00A0"}
                </motion.span>
              ))}
            </>
          )}
        </Select.Value>
        <Select.Icon className="flex">
          <ChevronDown className="size-4" />
        </Select.Icon>
      </Select.Trigger>
      <Select.Portal>
        <Select.Positioner className="outline-none" sideOffset={8}>
          <Select.Popup className="group origin-[var(--transform-origin)] rounded-md bg-white py-1 shadow-lg outline outline-neutral-200 transition-[transform,scale,opacity] data-[ending-style]:scale-100 data-[ending-style]:opacity-100 data-[ending-style]:transition-none data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[side=none]:data-[starting-style]:scale-100 data-[side=none]:data-[starting-style]:opacity-100 data-[side=none]:data-[starting-style]:transition-none">
            {exampleData.map((item) => (
              <SelectItem value={item} key={item}>
                {item}
              </SelectItem>
            ))}
          </Select.Popup>
        </Select.Positioner>
      </Select.Portal>
    </Select.Root>
  );
}
 
function SelectItem({
  className,
  children,
  ...props
}: React.ComponentPropsWithRef<typeof Select.Item>) {
  return (
    <Select.Item
      className={cn(
        "grid min-w-[var(--anchor-width)] cursor-default grid-cols-[1fr_0.75rem] items-center gap-2 py-2 pr-4 pl-2.5 text-sm leading-4 outline-none select-none group-data-[side=none]:min-w-[calc(var(--anchor-width)+1rem)] group-data-[side=none]:pr-12 group-data-[side=none]:text-base group-data-[side=none]:leading-4 data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-x-1 data-[highlighted]:before:inset-y-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-sm data-[highlighted]:before:bg-gray-900",
        className
      )}
      {...props}
    >
      <Select.ItemText>{children}</Select.ItemText>
      <Select.ItemIndicator>
        <Check className="size-3" />
      </Select.ItemIndicator>
    </Select.Item>
  );
}
 
export { SelectModel };