Pathfinderby Commenda
Components

Select

Single-value selection with local or remote search.

Installation

Import the component in your screen:

Example.tsx
import { Select } from '@commenda/pathfinder/select';

Load the stylesheet once in your app root, after host styles:

Example.tsx
import '@commenda/pathfinder/styles.css';

Pathfinder ships precompiled CSS; no Tailwind setup is required. In Next.js App Router, put interactive state and callbacks in a client component. See installation for the workspace setup.

Filled

Meridian UK Ltd

Clearable

Meridian UK Ltd

API reference

PropTypeBehavior
optionsConditionalreadonly ChoiceOption[]Exactly one source, stable string IDs
groupsConditionalreadonly ChoiceGroup[]Exactly one source, stable string IDs
valueConditionalstring|nullControlled callback required; null clears
defaultValuestring|nullControlled callback required; null clears
searchConfigConditionalObjectView fields · 2 shapesLocal search enabled above eight options
selectedOptionsreadonly ChoiceOption[]Remote metadata for selected IDs absent from results
paginationConfigChoicePaginationConfigView fieldsRemote-only; application owns cursors
renderConfigObjectView fieldsContent only; no interactive descendants
listConfigObjectView fields320px maximum by default; opt-in virtualization enables search
clearConfigObjectView fieldsOptional clear button
addItemConfigObjectView fieldsExplicit creation action; app supplies the resulting record
labelConditionalstringVisible field label. Supply an accessible name when it is omitted.
hideLabelbooleanVisually hide the label while preserving its accessible name.
aria-labelConditionalstringDefines a string value that labels the current element.
requiredConditionalbooleanMark the field as required. See validation guidance for the component.
labelConfigObjectView fields · 2 shapesChoose either optionalLabel or help, never both. never excludes the other field.
descriptionstringSupporting guidance associated with the field.
errorConfigObjectView fieldsValidation message and when it becomes visible.
disableConfigDisableConfigView fieldsDisabled state with an optional explanation.
idstringStable HTML identifier for the component.
namestring—
formstring—
autoCompletestring—
classNamestringAdditional classes for the component root.
placeholderstring—
onBlurFocusEventHandler<HTMLButtonElement>Called when focus leaves the component.
onFocusFocusEventHandler<HTMLButtonElement>Called when focus enters the component.
emptyLabelstring—
onValueChangeConditional( value: string | null ) => void—
searchConfigObject fields

Choose one of these 2 shapes. Do not combine fields excluded by the selected shape.

Option 1

FieldTypeRequirement
mode"local"Required
enabledbooleanOptional
placeholderstringOptional

Option 2

FieldTypeRequirement
mode"remote"Required
querystringRequired
onQueryChange(query: string) => voidRequired
resultsQuerystringRequired
isLoadingbooleanRequired
errorstringOptional
onRetry(() => void)Optional
placeholderstringOptional
paginationConfigObject fields
FieldTypeRequirement
hasMorebooleanRequired
isLoadingMorebooleanRequired
onLoadMore() => voidRequired
errorstringOptional
onRetry(() => void)Optional
renderConfigObject fields
FieldTypeRequirement
optionContent((option: ChoiceOption, state: { isSelected: boolean; }) => ReactNode)Optional
selectedContent((option: ChoiceOption) => ReactNode)Optional
listConfigObject fields
FieldTypeRequirement
virtualizedbooleanOptional
maxHeightnumberOptional
clearConfigObject fields
FieldTypeRequirement
labelstringOptional
addItemConfigObject fields
FieldTypeRequirement
labelstringRequired
onAddItem(query: string) => voidRequired
isDisabledbooleanOptional
labelConfigObject fields

Choose one of these 2 shapes. Do not combine fields excluded by the selected shape.

Option 1

FieldTypeRequirement
optionalLabelstringRequired

Do not supply: help.

Option 2

FieldTypeRequirement
helpstringRequired

Do not supply: optionalLabel.

errorConfigObject fields
FieldTypeRequirement
messagestringRequired
reveal"after-blur" | "immediate"Optional
disableConfigObject fields
FieldTypeRequirement
isDisabledbooleanRequired
reasonstringOptional

Shared labels, descriptions, required annotations, errorConfig, disableConfig, IDs, form names, refs, and focus/blur callbacks follow Input. Selection controls do not expose readOnly.

A ChoiceOption has value, label, optional description, keywords, leadingIcon, and disableConfig. Groups contain stable id, label, and options. Keywords participate only in local filtering. Rich rendering preserves plain-text labels for accessibility and search.

Local config: { mode: 'local', enabled?, placeholder? }. Small local lists use Base UI Select; searchable lists use Base UI Combobox internally. There are exactly two public selection components: Select and MultiSelect.

Remote config: { mode: 'remote', query, onQueryChange, resultsQuery, isLoading, error?, onRetry?, placeholder? }. Query updates immediately; the application owns debouncing, requests, cache, and cancellation. Remote results bypass client filtering and retain server order. While query differs from resultsQuery, stale options are unavailable for selection. Associate resultsQuery with the response that actually produced the options.

Supply selectedOptions for edit-form selections absent from current results. Selection never disappears merely because a page changes. A selected-only cache bridges user selection updates; it is not an unbounded history of every result. Missing metadata renders an explicit unavailable-value label until supplied.

Pagination receives accumulated, deduplicated options. New pages preserve scroll; new queries reset it. The UI gates duplicate load-more requests and provides a keyboard-accessible Load more button. Page errors preserve loaded results and offer retry. Virtualization limits mounted rows; remote pagination limits fetched data. Use both where appropriate.

Locked selected options survive tag removal, clear-all, Backspace, and bulk operations. MultiSelect select-all affects listed enabled options only and cannot imply selecting every record on a server. Bulk selection that would exceed a limit is disabled rather than partially applied.

Groups

States

Meridian UK Ltd
This filing is locked.
Choose an entity.

Large

Read selection and search rules for source provenance, API ownership, and constraints.

autoComplete forwards the native autocomplete token to the underlying selection control. Composite address fields use country and address-level1.

Design rules

Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.

Source rules

RuleFigma sourceContract
SELECT-001180:1177, 182:1206One selected value; popup 4px below trigger; search beyond about eight choices.
SELECT-002182:236Selected flag/icon matches option presentation; placeholder has no flag.

Sources: Search, Select, MultiSelect. The supplied Select node 182:1182 is its dimensions frame; 180:1177 is the actual component set.

Field geometry and states

The component sheets are Select 182:1216 and MultiSelect 865:3078.

  • SELECT-VISUAL-001: 28px field, 6px radius, 8px inner padding, 4px content gaps, 16px flag and chevron. Clear precedes the chevron (180:593). Empty fields have no leading flag.
  • MULTISELECT-VISUAL-001: 4px starting inset; selected chips and +N are 20px high with radius 4 and a 0.5px pearl 300 border. Chip icons/removal are 12px; the final chevron is 16px (180:620).
  • SELECTION-STATE-001: Pearl 400 default border; pearl 500 hover. Open fields retain blue 700 border and a 2px blue 200 ring while focus is in the popup (180:152, 180:766). Invalid uses red 600 at rest and red 700/red 200 when open. Error text is red 900 with a 12px alert icon (180:1031).
  • SELECTION-STATE-002: Disabled fields use pearl 100 background, pearl 300 border, pearl 600 values and pearl 800 labels. Disabled MultiSelect chips omit removal buttons (180:839).

The Figma 320px MultiSelect frame clips part of the second chip inside a 256px strip. The implementation deliberately truncates the final visible chip's label instead, preserving its removal button and full-label tooltip; a 96px minimum keeps partial chips readable before using +N. Placeholder text uses pearl 800 rather than source pearl 700 to meet text contrast on white. These are documented accessibility adjustments, not new design variants.

The entire field opens the list; removal and clear remain independent buttons. Simple and searchable paths share the same field rendering. Popups follow the embedded Dropdown instances 182:606 and 865:59996 (10px radius, 6px list inset, 2px row gap, 32px minimum rows, 36px flush search row with 12px horizontal inset); option roles remain Select/Combobox semantics. Multi-selection uses Pathfinder Checkbox in read-only presentation mode inside each option; the option owns selection and keyboard focus. Select-all is a functional Checkbox with mixed state. Single selection shows a trailing check. Search, clear and removal icons sit in explicit flex boxes so their centers align with the input and chip rows.

API ownership

Exactly Select and MultiSelect are public. Both support local search, server search, groups, rich option content, pagination, and virtualization. Dropdown remains an action menu. Free-text query entry belongs to Search; typed text never silently becomes a selected option ID.

The application owns query requests, debounce, cancellation, cursors, deduplicated page merging, and cache invalidation. Pathfinder blocks stale-query selection using query/resultsQuery, retains server ranking, and displays initial loading, empty, error, load-more, and retry states. Do not pass yesterday's query as resultsQuery for today's response.

Use a query-keyed data hook or AbortController plus request identity protection. A late response for an earlier query must not overwrite the active query's results. Reset cursors when the query changes. The interactive remote example uses a cancellable simulated transport so it works without an application endpoint; replace it with your authenticated API adapter.

Selected IDs and selectedOptions are separate from fetched pages. selectedOptions resolves labels/icons for saved values missing from the current result set. It does not pin unmatched rows to the results. The component caches only currently selected metadata as a bridge; applications remain responsible for authoritative record updates and deleted-record decisions.

Large lists and bulk actions

Enable listConfig.virtualized for large lists. This enables searchable interaction and measured-height rows; keyboard-highlighted options stay mounted. API pagination and virtualization solve different costs and can be combined. Option values must be globally unique and stable across groups and pages.

Select-all means currently listed, filtered, enabled results. Locked values survive all UI removal paths. A bulk selection that exceeds selectionLimitConfig.max is disabled; it never chooses an arbitrary subset. Selecting all matches across an entire backend requires a separate server-side selection model.

Custom renderers supply noninteractive content. Pathfinder owns trigger and option roles, events, refs, selection indicators, and focus. Add-item is a separate explicit action; application creation returns a real record and updates options/value.

Accessibility and forms

Labels, descriptions, error reveal, and disable reasons follow the shared field contract. Single selection serializes one ID; multiple selection serializes repeated names. Trigger refs and form reset work for both primitive paths. Searchable popups provide an explicitly named query input. Load more and retry are keyboard-accessible buttons outside the option list.

Tag remove buttons are siblings of the trigger, never nested inside it. Long selected labels remain available through tooltips. Search result compositions must handle overlay Escape before field clear/collapse through preventDefault.

Flags, chevrons, and clear/remove icons sit in explicit square flex containers with both axes centered. Do not rely on inline SVG baseline alignment or compensate with pixel offsets. The selected value, tags, and trailing controls share the field’s vertical center. Row Checkbox instances are inert visual indicators; the option owns selection and accessibility semantics.

Explicit differences and boundaries

  • Editors: Select and DatePicker retain their existing control heights; they are not pixel-identical to compact picker drawings. Text/number editors use the compact geometry. Async save errors remain visibly actionable with retry.

Motion and edge cases

Long titles, action labels and descriptions wrap. Bodies scroll within constrained viewports. Test nested Select/Popover, narrow screens, RTL, zoom, repeated Escape, pending/rejected close guards, pointer cancel, and focus return. Do not hide validation errors or close a form before submission succeeds.

Selection guidance

  • DROPDOWN-FUTURE-01 — Figma selection guidance (the historical rule ID is retained): search beyond about eight choices; empty state names the query; footer has one link action outside options. Multiple selection keeps the popup open and preserves row order. Selected rows have checks; highlight remains independent. These selection behaviors belong to Select and MultiSelect, not the action-oriented Dropdown.

On this page