Skip to content
OpenPerf
Esc
↑↓navigate↵open⌘Jpreview
On this page

Select

Picks one value, or several, from a short fixed list.

import { Select } from "@openperf/deft";

import { Preview } from "../../src/Preview";
import { OPTIONS } from "./data";

export default function SelectBasic() {
  return (
    <Preview overlay={true}>
      <Select.Root defaultValue="option-1" items={OPTIONS}>
        <Select.Trigger />
        <Select.Content>
          {OPTIONS.map((option) => (
            <Select.Item key={option.value} value={option.value}>
              {option.label}
            </Select.Item>
          ))}
        </Select.Content>
      </Select.Root>
    </Preview>
  );
}
  • After selection, the trigger shows the current value. Before selection, use a specific placeholder, not a generic label like “Select…”.
  • Use Combobox for lists longer than ten items or lists that people need to search.
  • Pass multiple to Select.Root for several values; a check marks the chosen ones.
  • The trigger takes the variants and sizes of Button. Inside a List, use variant="field".
  • The list opens below the trigger, at least as wide as it, and scrolls when it runs out of room.

Inline

import { Select } from "@openperf/deft";

import { Preview } from "../../src/Preview";
import { OPTIONS } from "./data";

export default function SelectInline() {
  return (
    <Preview overlay={true}>
      <p>
        Text with{" "}
        <Select.Root defaultValue="option-1" items={OPTIONS}>
          <Select.Trigger size="inline" variant="ghost" />
          <Select.Content>
            {OPTIONS.map((option) => (
              <Select.Item key={option.value} value={option.value}>
                {option.label}
              </Select.Item>
            ))}
          </Select.Content>
        </Select.Root>{" "}
        in a sentence.
      </p>
    </Preview>
  );
}

size="inline" with variant="ghost" sits inside a line of text and follows its size.

With pictures

import { Select } from "@openperf/deft";
import { useState } from "react";

import { Preview } from "../../src/Preview";

const OPTIONS = [
  { value: "accent", label: "Accent", color: "var(--accent-emphasis)" },
  { value: "warning", label: "Warning", color: "var(--chart-warning)" },
  { value: "danger", label: "Danger", color: "var(--chart-danger)" },
];

export default function SelectMedia() {
  const [value, setValue] = useState<null | string>("accent");

  return (
    <Preview overlay={true}>
      <Select.Root items={OPTIONS} onValueChange={setValue} value={value}>
        <Select.Trigger
          media={
            <span
              className="size-3.5 rounded-xs"
              style={{ background: OPTIONS.find((option) => option.value === value)?.color }}
            />
          }
        />
        <Select.Content>
          {OPTIONS.map((option) => (
            <Select.Item
              key={option.value}
              media={<span className="size-3.5 rounded-xs" style={{ background: option.color }} />}
              value={option.value}
            >
              {option.label}
            </Select.Item>
          ))}
        </Select.Content>
      </Select.Root>
    </Preview>
  );
}

media on Select.Item puts a swatch or a thumbnail before the label. Pass the chosen item’s picture to Select.Trigger with the same media prop so the trigger shows it too.

API

PropType
variant?"default" | "field" | "primary" | "ghost" | "danger"

On Trigger.

Type"default" | "field" | "primary" | "ghost" | "danger"
Default"default"
size?"inline" | "xs" | "sm" | "md"

On Trigger.

Type"inline" | "xs" | "sm" | "md"
Default"md"
placeholder?ReactNode

On Trigger. Shown before a value is picked.

TypeReactNode
children?ReactNode

On Trigger. Shown before the value, such as an icon or a label.

TypeReactNode
media?ReactNode

On Trigger and Item. A picture before the label, such as a swatch or a thumbnail.

TypeReactNode
align?"start" | "center" | "end"

On Content.

Type"start" | "center" | "end"
Default"start"
icon?TablerIcon

On Item.

TypeTablerIcon

Parts: all parts of Base UI Select, such as Root, Group and Value, with Deft’s Content, GroupLabel, Item, Separator and Trigger in place of the Base UI ones. Content renders the portal, positioner, popup and list.

Was this page helpful?