Select
Select supports two APIs:
Select+ sub-components: a lightweight, composable single-value listbox for simple forms and native parity.ReactSelect,ReactAsyncSelect,ReactCreatableSelect, andReactAsyncCreatableSelect: web-only wrappers aroundreact-selectfor searchable, async, multi-value, creatable, grouped, animated, and highly customized selection.
Use cases
- Form fields where the user picks exactly one value from a moderate-length list.
- Settings with mutually exclusive options that don't all need to be visible.
- Filters and sorting controls in toolbars.
- People, workflow, and tag selectors that need search, multi-select, remote loading, or new-value creation.
When NOT to use
- For short lists (2-5 options) where all should be visible — use RadioGroup.
- For persistent filter panels with many visible boolean values — use CheckboxGrid.
- For command search and app navigation — use Combobox or Command.
Import
import {
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectGroup,
SelectLabel,
SelectSeparator,
} from '@mihcm/ui/Select';import {
ReactSelect,
ReactAsyncSelect,
ReactCreatableSelect,
ReactAsyncCreatableSelect,
getReactSelectAnimatedComponents,
reactSelectAnimatedComponents,
type DefaultReactSelectOption,
} from '@mihcm/ui/Select';Basic usage
const [value, setValue] = useState('apple');
<Select value={value} onValueChange={setValue}>
<SelectTrigger placeholder="Pick a fruit" />
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="cherry">Cherry</SelectItem>
</SelectContent>
</Select>With groups
<Select value={value} onValueChange={setValue}>
<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>Composition
- Use
SelectGroupwithSelectLabelto organize options into named sections. SelectSeparatoradds a visual divider between groups.disabledonSelectItemprevents selection without removing the option from the list.- Use the React Select-powered exports when a select needs search, async data, multiple values, creatable values, rich labels, or menu portals.
React Select capability guide
The React Select-powered wrappers keep React Select's prop surface and add MiHCM defaults for unstyled, classNamePrefix, and token-based classNames.
| Capability | API | Use it for |
|---|---|---|
| Searchable select | ReactSelect isSearchable | People, country, office, and workflow pickers. |
| Multi-select | ReactSelect isMulti | Owners, tags, segments, permissions. |
| Async loading | ReactAsyncSelect loadOptions | Server-backed option lists, large directories. |
| Creatable values | ReactCreatableSelect | Tags, draft workflow names, controlled vocabularies. |
| Async + creatable | ReactAsyncCreatableSelect | Remote tag search with allowed local creation. |
| Grouped options | options={[{ label, options }]} | Separating teams, locations, statuses, or categories. |
| Rich option labels | formatOptionLabel or components | Metadata, avatars, status badges, secondary descriptions. |
| Controlled state | value, inputValue, menuIsOpen | Form libraries, guided flows, validation-heavy screens. |
| Styling overrides | classNames, className, classNamePrefix, styles | Tailwind token overrides without hardcoded colors. |
| Portal rendering | menuPortalTarget | Dialogs, sheets, tables, and clipped containers. |
| Animation | components={reactSelectAnimatedComponents} | Multi-value chip transitions. |
Popup select pattern
Use a popup select when selection is a filter or command attached to a toolbar trigger, not a form field. The trigger should summarize the current selection, and the popup should let people filter through options without leaving the current surface.
<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',
}}
/>Popup select rules:
- Use it in toolbars, table filters, issue queues, and quick filter panels.
- Keep trigger copy short:
Owner,Owner: Yashi, orOwners: 3 selected. - Use
controlShouldRenderValue={false}when the trigger should stay compact. - Use
closeMenuOnSelect={false}for multi-select filters. - Use
hideSelectedOptions={false}when selected options need to remain visible and toggleable. - Do not use popup select for required form fields; use the normal field-style select.
Web-only advanced usage
interface OwnerOption extends DefaultReactSelectOption {
meta: string;
}
<ReactSelect<OwnerOption, true>
isMulti
closeMenuOnSelect={false}
components={getReactSelectAnimatedComponents<OwnerOption, true>()}
options={ownerOptions}
classNames={{
control: ({ isFocused }) =>
cn('min-h-11 rounded-md border bg-background', isFocused && 'ring-2 ring-ring/20'),
multiValue: () => 'rounded-md bg-primary/10 text-primary',
}}
/><ReactAsyncCreatableSelect<OwnerOption, true>
isMulti
cacheOptions
defaultOptions
loadOptions={(inputValue) =>
fetch(`/api/owners?q=${encodeURIComponent(inputValue)}`).then((response) => response.json())
}
placeholder="Search or create owner..."
/>Security and accessibility
- The composable
Selectis controlled-first. When a user selects an item, the visible label updates from that selected item immediately instead of waiting for a later render cycle. - Validate remote
loadOptionsresponses with a schema before setting them as options. - Debounce or cancel remote requests in product code when the endpoint is expensive.
- Do not put secrets, internal IDs, access tokens, or raw model output in labels.
- Do not render raw HTML inside custom React Select components.
- Keep a visible label with
aria-labeloraria-labelledbywhen the surrounding form label is not programmatically connected. - Preserve keyboard behavior when replacing internal components through the
componentsprop.
Related
- Combobox — searchable dropdown for long option lists.
- RadioGroup — visible options for short lists (2-7 items).
- DropdownMenu — action list, not a form field.