codefast/ui

Command Palette

Search for a command to run...

Source
Form

Select

Accessible dropdown selector. Supports groups, disabled options, and custom triggers.

Examples

Align Item With Trigger

Use the position prop on SelectContent to control alignment. When position='item-aligned' (default), the popup positions so the selected item appears over the trigger. When position='popper', the popup aligns to the trigger edge.

Toggle to align the item with the trigger.

36 lines
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";
import { Switch } from "@codefast/ui/switch";
import * as React from "react";

export function SelectAlignItem() {
  const [alignItemWithTrigger, setAlignItemWithTrigger] = React.useState(true);

  return (
    <FieldGroup className="w-full max-w-xs">
      <Field orientation="horizontal">
        <FieldContent>
          <FieldLabel htmlFor="align-item">Align Item</FieldLabel>
          <FieldDescription>Toggle to align the item with the trigger.</FieldDescription>
        </FieldContent>
        <Switch id="align-item" checked={alignItemWithTrigger} onCheckedChange={setAlignItemWithTrigger} />
      </Field>
      <Field>
        <Select defaultValue="banana">
          <SelectTrigger>
            <SelectValue />
          </SelectTrigger>
          <SelectContent position={alignItemWithTrigger ? "item-aligned" : "popper"}>
            <SelectGroup>
              <SelectItem value="apple">Apple</SelectItem>
              <SelectItem value="banana">Banana</SelectItem>
              <SelectItem value="blueberry">Blueberry</SelectItem>
              <SelectItem value="grapes">Grapes</SelectItem>
              <SelectItem value="pineapple">Pineapple</SelectItem>
            </SelectGroup>
          </SelectContent>
        </Select>
      </Field>
    </FieldGroup>
  );
}

Disabled

Displays a list of options for the user to pick from—triggered by a button.

22 lines
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";

export function SelectDisabled() {
  return (
    <Select disabled>
      <SelectTrigger className="w-full max-w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          <SelectItem value="apple">Apple</SelectItem>
          <SelectItem value="banana">Banana</SelectItem>
          <SelectItem value="blueberry">Blueberry</SelectItem>
          <SelectItem value="grapes" disabled>
            Grapes
          </SelectItem>
          <SelectItem value="pineapple">Pineapple</SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  );
}

Groups

Use SelectGroup, SelectLabel, and SelectSeparator to organize items.

35 lines
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
} from "@codefast/ui/select";

export function SelectGroups() {
  return (
    <Select>
      <SelectTrigger className="w-full max-w-48">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          <SelectLabel>Fruits</SelectLabel>
          <SelectItem value="apple">Apple</SelectItem>
          <SelectItem value="banana">Banana</SelectItem>
          <SelectItem value="blueberry">Blueberry</SelectItem>
        </SelectGroup>
        <SelectSeparator />
        <SelectGroup>
          <SelectLabel>Vegetables</SelectLabel>
          <SelectItem value="carrot">Carrot</SelectItem>
          <SelectItem value="broccoli">Broccoli</SelectItem>
          <SelectItem value="spinach">Spinach</SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  );
}

Invalid

Set data-invalid on the Field and aria-invalid on the SelectTrigger to show an error state.

23 lines
import { Field, FieldError, FieldLabel } from "@codefast/ui/field";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";

export function SelectInvalid() {
  return (
    <Field data-invalid className="w-full max-w-48">
      <FieldLabel>Fruit</FieldLabel>
      <Select>
        <SelectTrigger aria-invalid>
          <SelectValue placeholder="Select a fruit" />
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            <SelectItem value="apple">Apple</SelectItem>
            <SelectItem value="banana">Banana</SelectItem>
            <SelectItem value="blueberry">Blueberry</SelectItem>
          </SelectGroup>
        </SelectContent>
      </Select>
      <FieldError>Please select a fruit.</FieldError>
    </Field>
  );
}

RTL

Right-to-left layout support for languages such as Arabic and Hebrew.

Translations are AI-generated for demonstration and may be imperfect.

111 lines
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
} from "@codefast/ui/select";
import * as React from "react";

import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";

const translations: Translations = {
  en: {
    dir: "ltr",
    values: {
      selectFruit: "Select a fruit",
      fruits: "Fruits",
      apple: "Apple",
      banana: "Banana",
      blueberry: "Blueberry",
      grapes: "Grapes",
      pineapple: "Pineapple",
      vegetables: "Vegetables",
      carrot: "Carrot",
      broccoli: "Broccoli",
      spinach: "Spinach",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      selectFruit: "اختر فاكهة",
      fruits: "الفواكه",
      apple: "تفاح",
      banana: "موز",
      blueberry: "توت أزرق",
      grapes: "عنب",
      pineapple: "أناناس",
      vegetables: "الخضروات",
      carrot: "جزر",
      broccoli: "بروكلي",
      spinach: "سبانخ",
    },
  },
  he: {
    dir: "rtl",
    values: {
      selectFruit: "בחר פרי",
      fruits: "פירות",
      apple: "תפוח",
      banana: "בננה",
      blueberry: "אוכמניה",
      grapes: "ענבים",
      pineapple: "אננס",
      vegetables: "ירקות",
      carrot: "גזר",
      broccoli: "ברוקולי",
      spinach: "תרד",
    },
  },
};

export function SelectRtl() {
  const { dir, t, language } = useTranslation(translations, "ar");
  const [selectedFruit, setSelectedFruit] = React.useState<string>("");

  const fruits = [
    { label: t.apple, value: "apple" },
    { label: t.banana, value: "banana" },
    { label: t.blueberry, value: "blueberry" },
    { label: t.grapes, value: "grapes" },
    { label: t.pineapple, value: "pineapple" },
  ];

  const vegetables = [
    { label: t.carrot, value: "carrot" },
    { label: t.broccoli, value: "broccoli" },
    { label: t.spinach, value: "spinach" },
  ];

  return (
    <Select value={selectedFruit} onValueChange={setSelectedFruit}>
      <SelectTrigger className="w-32" dir={dir}>
        <SelectValue placeholder={t.selectFruit} />
      </SelectTrigger>
      <SelectContent dir={dir} data-lang={dir === "rtl" ? language : undefined}>
        <SelectGroup>
          <SelectLabel>{t.fruits}</SelectLabel>
          {fruits.map((item) => (
            <SelectItem key={item.value} value={item.value}>
              {item.label}
            </SelectItem>
          ))}
        </SelectGroup>
        <SelectSeparator />
        <SelectGroup>
          <SelectLabel>{t.vegetables}</SelectLabel>
          {vegetables.map((item) => (
            <SelectItem key={item.value} value={item.value}>
              {item.label}
            </SelectItem>
          ))}
        </SelectGroup>
      </SelectContent>
    </Select>
  );
}

Scrollable

A select with many items that scrolls.

63 lines
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from "@codefast/ui/select";

export function SelectScrollable() {
  return (
    <Select>
      <SelectTrigger className="w-full max-w-64">
        <SelectValue placeholder="Select a timezone" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          <SelectLabel>North America</SelectLabel>
          <SelectItem value="est">Eastern Standard Time</SelectItem>
          <SelectItem value="cst">Central Standard Time</SelectItem>
          <SelectItem value="mst">Mountain Standard Time</SelectItem>
          <SelectItem value="pst">Pacific Standard Time</SelectItem>
          <SelectItem value="akst">Alaska Standard Time</SelectItem>
          <SelectItem value="hst">Hawaii Standard Time</SelectItem>
        </SelectGroup>
        <SelectGroup>
          <SelectLabel>Europe & Africa</SelectLabel>
          <SelectItem value="gmt">Greenwich Mean Time</SelectItem>
          <SelectItem value="cet">Central European Time</SelectItem>
          <SelectItem value="eet">Eastern European Time</SelectItem>
          <SelectItem value="west">Western European Summer Time</SelectItem>
          <SelectItem value="cat">Central Africa Time</SelectItem>
          <SelectItem value="eat">East Africa Time</SelectItem>
        </SelectGroup>
        <SelectGroup>
          <SelectLabel>Asia</SelectLabel>
          <SelectItem value="msk">Moscow Time</SelectItem>
          <SelectItem value="ist">India Standard Time</SelectItem>
          <SelectItem value="cst_china">China Standard Time</SelectItem>
          <SelectItem value="jst">Japan Standard Time</SelectItem>
          <SelectItem value="kst">Korea Standard Time</SelectItem>
          <SelectItem value="ist_indonesia">Indonesia Central Standard Time</SelectItem>
        </SelectGroup>
        <SelectGroup>
          <SelectLabel>Australia & Pacific</SelectLabel>
          <SelectItem value="awst">Australian Western Standard Time</SelectItem>
          <SelectItem value="acst">Australian Central Standard Time</SelectItem>
          <SelectItem value="aest">Australian Eastern Standard Time</SelectItem>
          <SelectItem value="nzst">New Zealand Standard Time</SelectItem>
          <SelectItem value="fjt">Fiji Time</SelectItem>
        </SelectGroup>
        <SelectGroup>
          <SelectLabel>South America</SelectLabel>
          <SelectItem value="art">Argentina Time</SelectItem>
          <SelectItem value="bot">Bolivia Time</SelectItem>
          <SelectItem value="brt">Brasilia Time</SelectItem>
          <SelectItem value="clt">Chile Standard Time</SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  );
}

Usage

The minimal import and composition — see Examples below for styled, real-world variants.

16 lines
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@codefast/ui/select";

export function SelectUsage() {
  return (
    <Select>
      <SelectTrigger className="w-40">
        <SelectValue placeholder="Select a fruit" />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="apple">Apple</SelectItem>
        <SelectItem value="banana">Banana</SelectItem>
        <SelectItem value="blueberry">Blueberry</SelectItem>
      </SelectContent>
    </Select>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

Select
├── SelectTrigger
│ └── SelectValue
└── SelectContent
└── SelectGroup
├── SelectLabel
└── SelectItem

Features

  • ARIA listbox with type-ahead — start typing to jump to a matching option.
  • position="item-aligned" (default) opens with the selected item over the trigger; position="popper" anchors to the trigger edge instead.
  • Two trigger sizes (default, sm) and built-in scroll-up/down buttons for long lists.
  • SelectGroup/SelectLabel/SelectSeparator organize options into announced groups.

API reference

Props for each part of the component. All native element props are also forwarded.

Select

Root. Owns the selected value.

valuestring

The controlled selected value.

defaultValuestring

The selected value when initially rendered (uncontrolled).

onValueChange(value: string) => void

Called when the selected value changes.

disabledboolean

Disables the whole control.

Defaultfalse

SelectGroup

Wraps related options so screen readers announce the group, not just visual spacing.

childrenReactNode

A SelectLabel followed by the group's SelectItems.

SelectLabel

Heading text for a SelectGroup.

childrenReactNode

Heading text for the group (required inside SelectGroup for accessibility).

SelectItem

valuestring

The value submitted when this item is chosen (required).

disabledboolean

Makes a single option non-selectable.

Defaultfalse

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

KeyFunction
SpaceOpens the menu when the trigger is focused.
Arrow+DownMoves to the next option.
Arrow+UpMoves to the previous option.
EnterSelects the highlighted option.
EscCloses without changing the value.
  • Implements the ARIA listbox pattern with type-ahead — start typing to jump to an option.
  • Use SelectLabel inside a SelectGroup so groups are announced, not just visually separated.
  • For native mobile behaviour with zero JS, prefer Native Select.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Always set a placeholder via SelectValue for empty state.
  • Group and label options once a list grows past ~7 items.

Don’t

  • Don’t use Select for boolean choices — use a Switch or Checkbox.
  • Don’t put interactive controls other than options inside the menu.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.