Primitive · CLI
Form Select
Soft-rect select on Base UI with caret rotate, popup settle, and list stagger ≤40ms.
Preview
npx shadcn@latest add @atroui/form-selectNo stack yet
Installation
Agent blurb — install command + no npm UI rule. For agents
npx shadcn@latest add @atroui/form-selectSource lands in your repo. Installation · Registry
Usage
Use for a short list of known options — roughly three to a dozen. Popup settles with menuPopupMotion; rows stagger ≤40ms; highlight morphs via layoutId. Value is fully controlled.
tsx
import { FormSelect } from "@/components/ui/form-select"
const [stack, setStack] = useState("")
<FormSelect
value={stack}
onValueChange={setStack}
placeholder="Pick a stack"
options={[
{ value: "next", label: "Next.js" },
{ value: "remix", label: "Remix" },
{ value: "astro", label: "Astro" },
]}
/>Label and disabled state
id lands on the trigger button, so htmlFor on a real label points at it. The placeholder is a hint, never a name - a field with only a placeholder is unlabelled for screen readers. disabled dims the trigger and blocks the popup; keep value at "" so the placeholder still explains why.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | Controlled value. "" shows the placeholder. |
| onValueChange | (value: string) => void | - | Change handler. |
| options | { value, label }[] | - | Selectable options. |
| placeholder | string | "Choose…" | Shown while no option is selected. |
| disabled | boolean | false | Dims the trigger and blocks the popup. |
| id | string | - | Forwarded to the trigger so a label can target it. |
| className | string | - | Extra classes on the trigger. |
Form Select