zeroUI

Select

The trigger is the same inset well as Input. The list drops from it in a square box lifted by a double outline; the highlighted row fills with the accent.

Install

terminal
# once per project: where @zeroui items come frompnpm dlx shadcn@latest registry add @zeroui=https://zeroui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zeroui/select

Examples

Basic

select-basic.tsx
import {  Select,  SelectContent,  SelectItem,  SelectTrigger,  SelectValue,} from "@/components/zero/select";const districts = {  harbor: "Harbor",  market: "Old market",  rail: "Rail yards",  canal: "Canal side",};export default function SelectBasic() {  return (    <Select items={districts}>      <SelectTrigger className="max-w-64">        <SelectValue placeholder="Pick a district" />      </SelectTrigger>      <SelectContent>        {Object.entries(districts).map(([value, label]) => (          <SelectItem key={value} value={value}>            {label}          </SelectItem>        ))}      </SelectContent>    </Select>  );}

Groups, a separator, a disabled row

select-groups.tsx
import {  Select,  SelectContent,  SelectGroup,  SelectItem,  SelectLabel,  SelectSeparator,  SelectTrigger,  SelectValue,} from "@/components/zero/select";const districts = {  harbor: "Harbor",  canal: "Canal side",  market: "Old market",  rail: "Rail yards",  tower: "Tower row",};export default function SelectGroups() {  return (    <Select items={districts} defaultValue="market">      <SelectTrigger className="max-w-64">        <SelectValue />      </SelectTrigger>      <SelectContent>        <SelectGroup>          <SelectLabel>Waterfront</SelectLabel>          <SelectItem value="harbor">Harbor</SelectItem>          <SelectItem value="canal">Canal side</SelectItem>        </SelectGroup>        <SelectSeparator />        <SelectGroup>          <SelectLabel>Inland</SelectLabel>          <SelectItem value="market">Old market</SelectItem>          <SelectItem value="rail">Rail yards</SelectItem>          <SelectItem value="tower" disabled>            Tower row          </SelectItem>        </SelectGroup>      </SelectContent>    </Select>  );}

In a Field

No crew is free at night this week.
select-field.tsx
import { Field, FieldError, FieldLabel } from "@/components/zero/field";import {  Select,  SelectContent,  SelectItem,  SelectTrigger,  SelectValue,} from "@/components/zero/select";const shifts = { dawn: "Dawn", day: "Day", night: "Night" };export default function SelectField() {  return (    <Field invalid className="w-full max-w-64">      <FieldLabel>Shift</FieldLabel>      <Select items={shifts} defaultValue="night">        <SelectTrigger>          <SelectValue />        </SelectTrigger>        <SelectContent>          {Object.entries(shifts).map(([value, label]) => (            <SelectItem key={value} value={value}>              {label}            </SelectItem>          ))}        </SelectContent>      </Select>      <FieldError match>No crew is free at night this week.</FieldError>    </Field>  );}

Behaviour

The popup grows out of the trigger in 240 ms and folds away in 140 ms. It always drops below (or above, if there is no room) rather than laying the chosen row over the trigger, so a long list scrolls inside it.

Pass items to Select (value to label) so the trigger can show the chosen label before the list has ever opened.

Props

Select

PropTypeDefaultDescription
itemsRecord<string, ReactNode>—Labels by value, for the trigger.

Also takes every prop of Base UI's Select.Root.

SelectTrigger

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"32, 40 or 52 px tall.
cut"end" | "start" | "both" | "none""end"Which edges of the well are cut on the slant.

Also takes every prop of Base UI's Select.Trigger.

SelectValue

PropTypeDefaultDescription
placeholderReactNode—Shown, in italic grey, until something is chosen.

Also takes every prop of Base UI's Select.Value.

SelectContent

PropTypeDefaultDescription
side"bottom" | "top" | "left" | "right""bottom"Where the popup opens from the trigger.
align"start" | "center" | "end""start"How it lines up with the trigger.

Also takes every prop of Base UI's Select.Popup.

SelectItem

Also takes every prop of Base UI's Select.Item.

SelectGroup, SelectLabel, SelectSeparator

Also takes every prop of Base UI's Select.Group.