Select

Dropdown for choosing one or more options.

import { Select, Searchable } from 'ui-lab-components/select';
import { Select } from 'ui-lab-components/select';
 
export default function Example() {
  return (
    <Select className="w-65">
      <Select.Trigger>
        <Select.Value placeholder="Select an option" />
      </Select.Trigger>
      <Select.Content>
        <Select.List>
          <Select.Item value="option1" textValue="Option 1">Option 1</Select.Item>
          <Select.Item value="option2" textValue="Option 2">Option 2</Select.Item>
          <Select.Item value="option3" textValue="Option 3">Option 3</Select.Item>
        </Select.List>
      </Select.Content>
    </Select>
  );
}
 

select/01-basic-select

A simple dropdown select component with options. Use this for form inputs and user choices.

Loading interactive preview
import { Select } from 'ui-lab-components/select';
 
export default function Example() {
  return (
    <Select>
      <Select.Trigger>
        <Select.Value placeholder="Select an option" />
      </Select.Trigger>
      <Select.Content>
        <Select.Item value="option1" textValue="Option 1">Option 1</Select.Item>
        <Select.Item value="option2" textValue="Option 2">Option 2</Select.Item>
        <Select.Item value="option3" textValue="Option 3">Option 3</Select.Item>
      </Select.Content>
    </Select>
  );
}
 

select/02-searchable

A filterable select component with search input. Type to filter through a large list of options.

Loading interactive preview
import { Select, Searchable } from 'ui-lab-components/select';
 
const countries = [
  { value: 'us', label: 'United States' },
  { value: 'ca', label: 'Canada' },
  { value: 'mx', label: 'Mexico' },
  { value: 'br', label: 'Brazil' },
  { value: 'ar', label: 'Argentina' },
  { value: 'uk', label: 'United Kingdom' },
  { value: 'fr', label: 'France' },
  { value: 'de', label: 'Germany' },
  { value: 'it', label: 'Italy' },
  { value: 'es', label: 'Spain' },
  { value: 'pt', label: 'Portugal' },
  { value: 'nl', label: 'Netherlands' },
  { value: 'be', label: 'Belgium' },
  { value: 'ch', label: 'Switzerland' },
  { value: 'at', label: 'Austria' },
  { value: 'se', label: 'Sweden' },
  { value: 'no', label: 'Norway' },
  { value: 'dk', label: 'Denmark' },
  { value: 'fi', label: 'Finland' },
  { value: 'pl', label: 'Poland' },
  { value: 'jp', label: 'Japan' },
  { value: 'cn', label: 'China' },
  { value: 'kr', label: 'South Korea' },
  { value: 'in', label: 'India' },
  { value: 'au', label: 'Australia' },
  { value: 'nz', label: 'New Zealand' },
];
 
export default function Example() {
  return (
    <Select>
      <Searchable.Input placeholder="Search countries..." />
      <Select.Content>
        {countries.map((country) => (
          <Select.Item key={country.value} value={country.value} textValue={country.label}>
            {country.label}
          </Select.Item>
        ))}
      </Select.Content>
    </Select>
  );
}
 

select/03-country

Button trigger reveals a searchable country list with flags and dial codes inside the dropdown content.

Loading interactive preview
"use client";
 
import { useState } from 'react';
import { Select, Searchable } from 'ui-lab-components/select';
 
const countries = [
  { value: "us", label: "United States", flag: "🇺🇸", code: "+1" },
  { value: "gb", label: "United Kingdom", flag: "🇬🇧", code: "+44" },
  { value: "de", label: "Germany", flag: "🇩🇪", code: "+49" },
  { value: "fr", label: "France", flag: "🇫🇷", code: "+33" },
  { value: "jp", label: "Japan", flag: "🇯🇵", code: "+81" },
  { value: "kr", label: "South Korea", flag: "🇰🇷", code: "+82" },
  { value: "cn", label: "China", flag: "🇨🇳", code: "+86" },
  { value: "in", label: "India", flag: "🇮🇳", code: "+91" },
  { value: "br", label: "Brazil", flag: "🇧🇷", code: "+55" },
  { value: "au", label: "Australia", flag: "🇦🇺", code: "+61" },
  { value: "ca", label: "Canada", flag: "🇨🇦", code: "+1" },
  { value: "mx", label: "Mexico", flag: "🇲🇽", code: "+52" },
];
 
const code = `<Select
  selectedKey={country}
  label={selected?.label}
  onSelectionChange={setCountry}
>
  <Select.Trigger>
    <Select.Value
      placeholder="Choose a country..."
      icon={selected && <span className="text-sm">{selected.flag}</span>}
    />
  </Select.Trigger>
  <Searchable.Content searchPlaceholder="Search countries...">
    {countries.map((country) => (
      <Select.Item
        key={country.value}
        value={country.value}
        textValue={country.label}
        icon={<span className="text-md">{country.flag}</span>}
      >
        <div className="flex w-full items-center justify-between">
          <span>{country.label}</span>
          <span className="ml-2 text-sm text-foreground-400">{country.code}</span>
        </div>
      </Select.Item>
    ))}
  </Searchable.Content>
</Select>`;
 
export default function Example() {
  const [country, setCountry] = useState<string | number | null>("us");
  const selected = countries.find(c => c.value === country);
 
  return (
    <Select
      selectedKey={country}
      label={selected?.label}
      onSelectionChange={setCountry}
    >
      <Select.Trigger>
        <Select.Value
          placeholder="Choose a country..."
          icon={selected && <span className="text-sm">{selected.flag}</span>}
        />
      </Select.Trigger>
      <Searchable.Content searchPlaceholder="Search countries...">
        {countries.map((c) => (
          <Select.Item key={c.value} value={c.value} textValue={c.label} icon={<span className="text-md">{c.flag}</span>}>
            <div className="flex items-center justify-between w-full">
              <span>{c.label}</span>
              <span className="ml-2 text-sm text-foreground-400">{c.code}</span>
            </div>
          </Select.Item>
        ))}
      </Searchable.Content>
    </Select>
  );
}
 

select/04-chip-multi-pick

A multi-select that renders current values as dismissible chips and lets users add more items from the dropdown.

Loading interactive preview
"use client";
 
import { useMemo, useState } from 'react';
import { Badge } from 'ui-lab-components/badge';
import { Flex } from 'ui-lab-components/flex';
import { Select, Searchable } from 'ui-lab-components/select';
 
const options = [
  {
    value: 'approval-queue',
    label: 'Approval Queue',
    description: 'Review requests and route decisions to the right owner.',
  },
  {
    value: 'ai-assistant',
    label: 'AI Assistant',
    description: 'Help teammates summarize records and draft next steps.',
  },
  {
    value: 'customer-lookup',
    label: 'Customer Lookup',
    description: 'Find accounts, contacts, and recent activity in one place.',
  },
  {
    value: 'data-import',
    label: 'Data Import',
    description: 'Turn uploaded CSV files into validated workspace records.',
  },
  {
    value: 'audit-log',
    label: 'Audit Log',
    description: 'Keep a searchable history of sensitive changes and actions.',
  },
  {
    value: 'role-access',
    label: 'Role Access',
    description: 'Control which tools and data each teammate can use.',
  },
  {
    value: 'webhooks',
    label: 'Webhooks',
    description: 'Notify other systems when important workflow events happen.',
  },
  {
    value: 'command-palette',
    label: 'Command Palette',
    description: 'Let power users jump to records and actions from the keyboard.',
  },
];
 
export default function Example() {
  const [selectedKeys, setSelectedKeys] = useState<Array<string | number>>([
    'approval-queue',
    'ai-assistant',
  ]);
 
  const selectedSet = useMemo(() => new Set(selectedKeys), [selectedKeys]);
  const selectedOptions = useMemo(
    () => options.filter((option) => selectedSet.has(option.value)),
    [selectedSet]
  );
 
  return (
    <Select
      mode="multiple"
      selectedKeys={selectedKeys}
      onSelectionChange={setSelectedKeys}
      className="w-full max-w-xl"
      label={selectedOptions.map((option) => option.label).join(', ')}
    >
      <Select.Trigger>
        <Select.Value>
          {() => (
            <Flex gap="xs" wrap align-center>
              {selectedOptions.length > 0 ? (
                selectedOptions.map((option) => (
                  <Badge
                    key={option.value}
                    className="min-h-7 gap-1 px-2 text-sm"
                    dismissible
                    onDismiss={() => setSelectedKeys((prev) => prev.filter((value) => value !== option.value))}
                  >
                    {option.label}
                  </Badge>
                ))
              ) : (
                <span className="text-foreground-400">Add features to your internal app</span>
              )}
            </Flex>
          )}
        </Select.Value>
      </Select.Trigger>
      <Searchable.Content searchPlaceholder="Search app features...">
        <Select.List>
          {options.map((option) => (
            <Select.Item
              key={option.value}
              value={option.value}
              textValue={option.label}
              description={option.description}
            >
              {option.label}
            </Select.Item>
          ))}
        </Select.List>
      </Searchable.Content>
    </Select>
  );
}
 

select/05-mention-completion

useCompletion turns Select.Content into an @mention menu anchored to the trigger character inside a textarea, driven by the textarea's keyboard.

Loading interactive preview
'use client';
 
import { useState } from 'react';
import { Icon } from 'ui-lab-components/icon';
import { Input } from 'ui-lab-components/input';
import { Select, useCompletion } from 'ui-lab-components/select';
 
const people = [
  { handle: 'maya', name: 'Maya Chen' },
  { handle: 'alex', name: 'Alex Rivera' },
  { handle: 'sam', name: 'Sam Patel' },
  { handle: 'jordan', name: 'Jordan Lee' },
];
 
export default function Example() {
  const [value, setValue] = useState('');
  const completion = useCompletion({ value, onValueChange: setValue, triggers: ['@'] });
 
  return (
    <Select
      state={{ open: completion.open }}
      selectedKey={null}
      onSelectionChange={(handle) => completion.complete(`@${handle} `)}
    >
      <Select.Content completion={completion} emptyContent="No one by that name">
        {people.map((person) => (
          <Select.Item
            key={person.handle}
            value={person.handle}
            textValue={`${person.handle} ${person.name}`}
            icon={<Icon id="agent" size={16} />}
          >
            <span className="flex w-full min-w-44 items-center justify-between gap-6">
              {person.name}
              <span className="text-foreground-400">@{person.handle}</span>
            </span>
          </Select.Item>
        ))}
      </Select.Content>
      <Input
        ref={completion.fieldProps.ref}
        aria-label="Comment"
        placeholder="Leave a comment, type @ to mention"
        value={value}
        onChange={(event) => setValue(event.target.value)}
        onSelect={completion.fieldProps.onSelect}
        onKeyDown={completion.fieldProps.onKeyDown}
      />
    </Select>
  );
}