Select
Pick one value from a short, fixed list that doesnโt need searching.
When to use it
Use Select for five to fifteen fixed options, such as a role, a region, or a sort order. People can open it, scan it, and pick in one motion.
With fewer options worth comparing, show them all with Radio Group. With more than people can scan, let them search with Combobox.
Radio Group
One choice, every option visible.
Visibility
Only you can see this project.
Everyone on the Design team can view and edit.
Anyone in the workspace can find it.
Two to five options worth comparing.
SelectThis page
A short list, one value.
A role, a region, or a sort order.
How it behaves
Arrow keys move between options, typing finds an option, and Escape dismisses the list.
Base UI manages focus and the hidden form input. Provide name for form submission and items for readable selected labels.
Connect SelectTrigger to a visible label. Use value/onValueChange for controlled state or defaultValue for local state.
SelectTrigger variant="subtle" sizes to its label with a filled surface and up/down chevrons; the default remains an outlined form field. SelectItem description adds supporting body-sm text without changing the selected label.
Options sit inside a padded list with the same neutral muted hover surface as Dropdown Menu. A leading checkmark identifies the selected value independently of keyboard or pointer highlight.
SelectContent footer places actions outside the listbox. Use controlled open/onOpenChange to close after a footer action; listClassName customizes the scrollable list.
Build it
Add it with the shadcn CLI:
1pnpm dlx shadcn@latest add https://jzbui.com/r/select.jsonA complete example. Change the values to fit your product:
123456789101112131415161718192021222324252627282930313233"use client"
import { useId, useState } from "react"import { ArrowRightLeft } from "lucide-react"import { Button } from "@/components/ui/button"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
const items = [ { value: "personal", label: "Personal workspace", description: "A private space for your own projects." }, { value: "team", label: "Team workspace", description: "Shared projects, members, and team settings." },]
export function WorkspacePicker({ onCompare }: { onCompare: () => void }) { const id = useId() const [open, setOpen] = useState(false) return ( <Select items={items} defaultValue="personal" name="workspace" open={open} onOpenChange={setOpen}> <label htmlFor={id}>Workspace</label> <SelectTrigger id={id} variant="subtle"><SelectValue /></SelectTrigger> <SelectContent className="w-80" footer={ <Button variant="ghost" className="w-full justify-start rounded-md" onClick={() => { setOpen(false); onCompare() }}> <ArrowRightLeft aria-hidden="true" /> Compare workspaces </Button> }> {items.map((item) => ( <SelectItem key={item.value} value={item.value} description={item.description}> {item.label} </SelectItem> ))} </SelectContent> </Select> )}The full source is yours to edit once installed: