MiHCM
primitives/@mihcm/ui·v0.21.0·stable

Select

Custom dropdown for single-value selection. Composable: Select, SelectTrigger, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator.

In context

Workspace region picker, inline sort selector, role assignment with destructive tone option, and a grouped tech-stack picker with section labels and separators.

Workspace region

Single-value select with placeholder.

Data residency follows region. Cannot be changed after provisioning.

Inbox sort

Inline trigger paired with a filter chip row.

Showing 248 of 1,284 threads

Assign role

Tone shifts to destructive on a role removal action.

Editorial access; cannot manage members.

Project tech stack

Grouped categories with separators.

Default

import { Select, SelectTrigger, SelectContent, SelectItem } from '@mihcm/ui/Select';
 
<Select value="apple" onValueChange={console.log}>
  <SelectTrigger placeholder="Pick a fruit" />
  <SelectContent>
    <SelectItem value="apple">Apple</SelectItem>
    <SelectItem value="banana">Banana</SelectItem>
    <SelectItem value="cherry">Cherry</SelectItem>
  </SelectContent>
</Select>

React Select searchable

People Ops14 workflows
import { ReactSelect, type DefaultReactSelectOption } from '@mihcm/ui/Select';
 
interface PeopleOption extends DefaultReactSelectOption {
  meta: string;
}
 
<ReactSelect<PeopleOption>
  options={groupedPeopleOptions}
  defaultValue={groupedPeopleOptions[0].options[0]}
  formatOptionLabel={(option) => (
    <div className="flex min-w-0 items-center justify-between gap-3">
      <span className="truncate font-medium">{option.label}</span>
      <span className="shrink-0 text-xs text-muted-foreground">{option.meta}</span>
    </div>
  )}
  placeholder="Search team or office..."
  isSearchable
/>

React Select multi with animation

People Ops
Recruiting
import {
  ReactSelect,
  getReactSelectAnimatedComponents,
} from '@mihcm/ui/Select';
 
<ReactSelect
  isMulti
  closeMenuOnSelect={false}
  components={getReactSelectAnimatedComponents()}
  defaultValue={[peopleOptions[0], peopleOptions[2]]}
  options={peopleOptions}
  placeholder="Assign owners..."
/>

React Select creatable

Salary review
import { ReactCreatableSelect } from '@mihcm/ui/Select';
 
<ReactCreatableSelect
  defaultValue={{ value: 'salary-review', label: 'Salary review' }}
  options={[
    { value: 'salary-review', label: 'Salary review' },
    { value: 'onboarding', label: 'Onboarding' },
    { value: 'performance', label: 'Performance' },
  ]}
  placeholder="Choose or create workflow..."
/>

React Select async

Load people data...
import { ReactAsyncSelect } from '@mihcm/ui/Select';
 
const loadPeopleOptions = (inputValue: string) =>
  fetch(`/api/people-options?q=${encodeURIComponent(inputValue)}`)
    .then((response) => response.json());
 
<ReactAsyncSelect
  cacheOptions
  defaultOptions
  loadOptions={loadPeopleOptions}
  placeholder="Load people data..."
/>

React Select async creatable

Search or create tags...
import { ReactAsyncCreatableSelect } from '@mihcm/ui/Select';
 
<ReactAsyncCreatableSelect
  isMulti
  cacheOptions
  defaultOptions
  loadOptions={loadPeopleOptions}
  placeholder="Search or create tags..."
/>

Use this pattern for toolbar filters where the trigger summarizes the current selection and opens a searchable popup list. It follows the Atlassian popup-select behavior: a compact trigger, filtering inside the popup, multiple selection, rich option rows, and selected items kept visible.

Owner: Yashi

Use this pattern for toolbar filters where the trigger summarizes the current selection.

import { ReactSelect, type DefaultReactSelectOption } from '@mihcm/ui/Select';
 
interface OwnerOption extends DefaultReactSelectOption {
  description: string;
  initials: string;
  status: 'Available' | 'Busy' | 'Away';
}
 
function OwnerOptionLabel(option: OwnerOption) {
  return (
    <div className="flex min-w-0 items-center gap-3">
      <span className="flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold text-foreground">
        {option.initials}
      </span>
      <span className="min-w-0 flex-1">
        <span className="block truncate text-sm font-medium text-foreground">{option.label}</span>
        <span className="block truncate text-xs text-muted-foreground">{option.description}</span>
      </span>
      <span className="shrink-0 rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
        {option.status}
      </span>
    </div>
  );
}
 
<ReactSelect<OwnerOption, true>
  aria-label="Filter by owner"
  isMulti
  closeMenuOnSelect={false}
  hideSelectedOptions={false}
  controlShouldRenderValue={false}
  value={selectedOwners}
  onChange={setSelectedOwners}
  options={ownerOptions}
  formatOptionLabel={OwnerOptionLabel}
  placeholder={selectedOwners.length > 0 ? `Owners: ${selectedOwners.length} selected` : 'Owner'}
  classNames={{
    control: ({ isFocused }) =>
      cn(
        'min-h-10 w-fit min-w-48 cursor-pointer rounded-md border bg-background px-2.5 text-body-sm shadow-sm',
        'hover:bg-muted',
        isFocused ? 'border-primary ring-2 ring-ring/20' : 'border-border',
      ),
    placeholder: () => 'font-medium text-foreground',
  }}
/>

Sizes

<Select value="md" onValueChange={console.log}>
  <SelectTrigger size="sm" placeholder="Small" />
  <SelectContent>
    <SelectItem value="sm">Small</SelectItem>
    <SelectItem value="md">Medium</SelectItem>
  </SelectContent>
</Select>
 
<Select value="md" onValueChange={console.log}>
  <SelectTrigger size="lg" placeholder="Large" />
  <SelectContent>
    <SelectItem value="md">Medium</SelectItem>
    <SelectItem value="lg">Large</SelectItem>
  </SelectContent>
</Select>

Destructive variant

<Select value="" onValueChange={console.log}>
  <SelectTrigger variant="destructive" placeholder="Select action" />
  <SelectContent>
    <SelectItem value="delete">Delete account</SelectItem>
    <SelectItem value="deactivate">Deactivate account</SelectItem>
  </SelectContent>
</Select>

With disabled items

<Select value="active" onValueChange={console.log}>
  <SelectTrigger placeholder="Filter by status" />
  <SelectContent>
    <SelectItem value="active">Active</SelectItem>
    <SelectItem value="pending">Pending</SelectItem>
    <SelectItem value="archived" disabled>Archived</SelectItem>
  </SelectContent>
</Select>

With groups and separators

import {
  Select, SelectTrigger, SelectContent,
  SelectItem, SelectGroup, SelectLabel, SelectSeparator,
} from '@mihcm/ui/Select';
 
<Select value="fe" onValueChange={console.log}>
  <SelectTrigger placeholder="Choose a role" />
  <SelectContent>
    <SelectGroup>
      <SelectLabel>Engineering</SelectLabel>
      <SelectItem value="fe">Frontend</SelectItem>
      <SelectItem value="be">Backend</SelectItem>
    </SelectGroup>
    <SelectSeparator />
    <SelectGroup>
      <SelectLabel>Design</SelectLabel>
      <SelectItem value="ux">UX Designer</SelectItem>
      <SelectItem value="ui">UI Designer</SelectItem>
    </SelectGroup>
  </SelectContent>
</Select>