Skip to content

Config

createSelkit(config) and every adapter accept a SelkitConfig. All options are optional.

SelkitConfig

OptionTypeDefaultDescription
optionsSelkitItem<T>[][]Initial options — flat options or groups.
valueSelkitValuenullInitial selection.
multiplebooleanfalseAllow multiple selection.
searchablebooleantrueWhether the list can be searched.
minResultsForSearchnumber0Show the search box only once the option count reaches this.
minInputLengthnumber0Minimum characters before filtering / loading.
fuzzybooleanfalseUse fuzzy subsequence matching instead of substring. Ignored if filter is set.
filterFilterFn<T>substringCustom match predicate (option, query) => boolean.
sorterSorterFn<T>Custom result ordering (a, b, query) => number (e.g. relevance). Flat lists only — ignored when options are grouped. Also a Vue/React prop.
hideSelectedbooleanfalseRemove chosen options from the list.
clearablebooleantrue singleShow a clear button.
closeOnSelectbooleantrue single / false multipleClose the dropdown after selecting.
disabledbooleanfalseDisable the control.
placeholderstringPlaceholder text.
ariaLabelstringAccessible name (aria-label) for the search input. Falls back to placeholder. Set one of them so screen readers (and axe) can identify the field.
loadOptions(query, page, { signal }) => Promise<SelkitItem<T>[] | SelkitLoadResult<T>>Async / paginated loading. The signal is aborted when superseded. See Async.
debouncenumber250Debounce in ms for loadOptions.
filterRemotebooleanfalseApply the local filter to remote results.
cachebooleanfalseMemoize the first page of loadOptions results by query. First page only; cleared by setOptions. See Async › Caching.
cacheTTLnumberMilliseconds before a cache entry is stale. Omit = never expires. Only with cache: true.
resolveSelected(values) => SelkitOption<T>[] | Promise<SelkitOption<T>[]>Look up full options for initial values whose options aren't loaded yet, so their labels can render. Sync or async; runs once at init; resolved options only fill selected labels (not the option pool). See Async › Prefilling.
taggablebooleanfalseAllow creating options on the fly.
createTag(query) => SelkitOption<T>Factory for new tags.
isValidToken(query) => booleanGate tag creation. Returning false silently hides the create row and blocks Enter / token separators. See Tagging › Validating.
tokenSeparatorsstring[][]Auto-split typed/pasted input on these separators (e.g. [',', ' ']) into tags. Multiple-select only; creating new tags also needs taggable. Tokens matching an existing option are selected; the remainder stays in the input.
restoreOnBackspacebooleanfalseMultiple-select only: when the input is empty, Backspace removes the last tag and restores its label to the input for editing (and opens the dropdown).
maxSelectionsnumberCap on selected items.
messagesPartial<SelkitMessages>English defaultsOverride the empty-state messages (loading / no results / type-more). See i18n.

i18n / messages

The dropdown's empty-state text is resolved by the core via controller.getEmptyMessage(), so every adapter shows the same string. Override any subset of the three messages — unspecified keys keep their English default.

ts
createSelkit({
  minInputLength: 3,
  messages: {
    loading: '載入中…',
    noResults: '查無資料',
    // `remaining` is how many more characters are still needed
    minInputLength: (remaining) => `再輸入 ${remaining} 個字`,
  },
})

getEmptyMessage() picks one message based on the current state, in order:

  1. loading — while async loadOptions is in flight.
  2. minInputLength(remaining) — when the query is shorter than minInputLength.
  3. noResults — otherwise (no matching options).

create(query) is separate: it labels the visible "create new item" row shown in the list when taggable is on (see Selection › Tagging).

ts
interface SelkitMessages {
  loading: string
  noResults: string
  minInputLength: (remaining: number) => string
  create: (query: string) => string // defaults to `Add "${query}"`
  // aria-live announcements (read by screen readers)
  selected: (label: string) => string //   `${label} selected`
  deselected: (label: string) => string // `${label} removed`
  cleared: () => string //                  'Selection cleared'
  resultsCount: (count: number) => string //'N results available'
}

The selected / deselected / cleared / resultsCount keys label the live-region announcements for screen readers.

DOM-only options

SelkitDomConfig (used by @selkit/dom) extends the above:

OptionTypeDefaultDescription
classPrefixstring"selkit"BEM class prefix.
namestringMaintain hidden inputs for form submission.
checkboxesbooleanfalseMultiple-select only: show a checkbox tick on each option (adds the selkit--checkboxes modifier; styling lives in the theme). Also a Vue/React prop.
autogrowbooleanfalseGrow the search input to fit its text (via the size attribute) instead of stretching to fill. Adds the selkit--autogrow modifier. Also a Vue/React prop.
dropdownAutoWidthbooleanfalseSize the dropdown to its content (at least as wide as the control, wider if needed) instead of matching the control width. Adds the selkit--auto-width modifier. Also a Vue/React prop.
virtualScrollbooleanfalseRender only the visible slice.
itemHeightnumber36Fixed row height for virtual scroll.
dropdownParentHTMLElement | stringPortal the dropdown into another element to escape clipping ancestors. Also a Vue/React prop.
positionerPositionerFactorybuilt-inSwap the dropdown positioner. Pass createFloatingPositioner from @selkit/floating for flip / shift / size collision handling. Also a Vue/React prop. See Positioning.
templateOption(option, meta) => string | NodeCustomize a dropdown option's content. Strings are set as text; return a Node for markup (icons). meta is { index, active, selected }. DOM-only — see renderOption / option slot for Vue/React.
templateSelection(option, meta) => string | NodeCustomize the selected tag / single-value content. Strings are set as text; return a Node for markup (icons). meta is { index, multiple }. DOM-only — see renderSelection / selection slot for Vue/React.

The Vue and React components expose the same options as props, plus virtualScroll / itemHeight and framework-specific bits. Customize a dropdown option with renderOption (React) / the option slot (Vue) / templateOption (DOM), and the selected display with renderSelection (React) / the selection slot (Vue) / templateSelection (DOM). Option meta is { index, active, selected }; selection meta is { index, multiple }.

Swappable components

Beyond option/selection content, you can replace the content of the structural chrome — the dropdown arrow, clear button, tag remove button, group heading and the empty/loading row. Each adapter uses its idiomatic mechanism, and only the inner content is swapped — the wrappers, classes and behavior (click handling, event delegation) stay intact, so you can't accidentally break the clear/remove buttons.

PartDOM configVue slotReact propMeta
Dropdown arrowtemplateArrowarrowrenderArrow{ open }
Clear buttontemplateClearclearrenderClear
Tag remove buttontemplateTagRemovetag-removerenderTagRemove(option, { index })
Group headingtemplateGroupgrouprenderGroup{ label, disabled }
Empty / loadingtemplateEmptyemptyrenderEmpty{ reason, message, query }

DOM hooks return string | Node (strings are set as text for XSS safety, Nodes are appended); Vue slots and React props return their native node types. The empty hook's reason is 'loading' \| 'min-input' \| 'no-results' and message is the resolved default text — branch on reason to show a spinner while loading and fall back to message otherwise. These are presentation-only, so they live entirely in the adapters (not in @selkit/core).

Types

ts
interface SelkitOption<T = unknown> {
  value: string | number
  label: string
  disabled?: boolean
  data?: T
}

interface SelkitGroup<T = unknown> {
  label: string
  disabled?: boolean
  options: SelkitOption<T>[]
}

type SelkitItem<T = unknown> = SelkitOption<T> | SelkitGroup<T>

type SelkitValue = string | number | null | Array<string | number>

type FilterFn<T = unknown> = (option: SelkitOption<T>, query: string) => boolean

type SorterFn<T = unknown> = (
  a: SelkitOption<T>,
  b: SelkitOption<T>,
  query: string,
) => number

interface SelkitLoadResult<T = unknown> {
  items: SelkitItem<T>[]
  hasMore: boolean
}

Released under the MIT License.