---
title: createBaseSelectSearchInput
description: Bind a custom text input to BaseSelect with accessible keyboard and
  IME behavior.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# createBaseSelectSearchInput

> Bind a custom text input to BaseSelect with accessible keyboard and IME behavior.

`createBaseSelectSearchInput` connects a caller-owned `<input>` to a `BaseSelect`. Call `BaseSelect.useContext()` inside a BaseSelect child component and pass that state to the hook. It supplies input attributes, keyboard handling, focus registration, and IME-safe query commits.

## Import

```tsx
import { createBaseSelectSearchInput } from 'moraine/utils'
```

## Options and result

```ts
interface BaseSelectSearchInputOptions {
  state: Pick<
    BaseSelectT.Context,
    | 'id'
    | 'listboxId'
    | 'open'
    | 'setOpen'
    | 'highlightedValue'
    | 'itemId'
    | 'locked'
    | 'disabled'
    | 'readOnly'
    | 'focusOwner'
    | 'setFocusOwner'
    | 'registerCompositionDiscarder'
    | 'keyDown'
    | 'focus'
    | 'blur'
  >
  searchValue: Accessor<string>
  setSearchValue: (value: string) => string
  enabled?: Accessor<boolean>
  displayValue?: Accessor<string>
  transformInput?: (value: string) => string
  maxLength?: number
  autocomplete?: JSX.InputHTMLAttributes<HTMLInputElement>['autocomplete']
}

const { inputProps, isComposing, discardComposition } = createBaseSelectSearchInput(options)
```

Pass a Solid signal as `searchValue` and its setter as `setSearchValue`, or provide equivalent functions. `setSearchValue` must return the text it applies. Spread `inputProps` directly on an `<input>`. The other result methods let a custom control defer its own keyboard work during composition or discard a draft before clearing the selection.

`enabled` defaults to `true`. When false, the input is read-only and BaseSelect handles keys as a non-editable control. `displayValue` defaults to `searchValue`; a Combobox can supply the selected label while closed. `transformInput` runs before a completed input is committed. `maxLength` sets the native input attribute; clamp the value in your setter too when programmatic updates must obey the same limit. `autocomplete` defaults to `off`.

## Example

```tsx title="src/CustomSearchSelect.tsx"
import { createSignal, For } from 'solid-js'
import { BaseSelect } from 'moraine'
import { createBaseSelectSearchInput } from 'moraine/utils'
import type { BaseSelectSearchInputOptions } from 'moraine/utils'

const fruits = [
  { value: 'apple', label: 'Apple' },
  { value: 'banana', label: 'Banana' },
  { value: 'cherry', label: 'Cherry' },
]

function SearchControl(
  props: Pick<BaseSelectSearchInputOptions, 'searchValue' | 'setSearchValue'>,
) {
  const state = BaseSelect.useContext()
  const { inputProps } = createBaseSelectSearchInput({
    state,
    searchValue: () => props.searchValue(),
    setSearchValue: (value) => props.setSearchValue(value),
  })
  return (
    <BaseSelect.Control>
      <input {...inputProps} placeholder="Search fruits" />
    </BaseSelect.Control>
  )
}

export function CustomSearchSelect() {
  const [searchValue, setSearchValue] = createSignal('')
  const matches = () =>
    fruits.filter((fruit) => fruit.label.toLowerCase().includes(searchValue().toLowerCase()))

  return (
    <BaseSelect items={matches()}>
      <SearchControl searchValue={searchValue} setSearchValue={setSearchValue} />
      <BaseSelect.Content>
        <BaseSelect.Listbox>
          <For each={matches()}>
            {(fruit) => <BaseSelect.Item item={fruit}>{fruit.label}</BaseSelect.Item>}
          </For>
        </BaseSelect.Listbox>
      </BaseSelect.Content>
    </BaseSelect>
  )
}
```

