esc

Type to search...

Component exports

Components

Prop Type Default Description
SearchableDropdown<T, G> Component Single-select searchable dropdown. T is the option type (string or object), G is the group context type.
SearchableDropdownMulti<T, G> Component Multi-select variant with chip display, clear-all, and backspace deletion.
imports.ts
import {
  SearchableDropdown,
  SearchableDropdownMulti,
} from "@luciodale/react-searchable-dropdown";

Hook exports

Hooks

Prop Type Default Description
useDebounce<T>(value, delay) Hook Returns [debouncedValue, setDebouncedValue]. When delay is 0, updates are immediate. Useful for debouncing external state that feeds into the dropdown.
useDebounceExample.tsx
import { useDebounce } from "@luciodale/react-searchable-dropdown";

function MyComponent() {
  const [query, setQuery] = useState("");
  const [debouncedQuery] = useDebounce(query, 200);

  // debouncedQuery updates 200ms after the last query change
}

Option types

types.ts
// Base option type — either a string or an object
type TDropdownOption = TObjectLikeDropdownOption | TStringDropdownOption;

// String options
type TStringDropdownOption = string;

// Object options must have label and value
type TObjectDropdownOption = {
  [key: string]: string | number | boolean;
  label: string;
  value: string;
};

// Created-on-the-fly options (when no match is found)
type TNewValueDropdownOption = {
  isNewValue: true;
  label: string;
  value: string;
};

// Union of object option types
type TObjectLikeDropdownOption =
  | TObjectDropdownOption
  | TNewValueDropdownOption;

Search types

search-types.ts
// Constrains searchOptionKeys to actual keys of the option type
type TSearchOptionKeys<T extends TDropdownOption> =
  T extends { label: string; value: string }
    ? Array<Extract<keyof T, string>>
    : never;

// Match-sorter ranking keys
type TSearchableDropdownFilterType = keyof typeof matchSorter.rankings;
// Values: "CASE_SENSITIVE_EQUAL" | "EQUAL" | "STARTS_WITH"
//       | "WORD_STARTS_WITH" | "CONTAINS" | "ACRONYM"
//       | "MATCHES" | "NO_MATCH"

Group types

group-types.ts
// Discriminated union — provide both or neither
type TGroups<T extends TDropdownOption, G> =
  | {
      handleGroups: (
        matchingOptions: T[]
      ) => {
        groupCounts: number[];
        groupCategories: string[];
      };
      groupContent: (
        index: number,
        groupCategories: string[],
        context: G
      ) => React.ReactNode;
    }
  | {
      handleGroups?: undefined;
      groupContent?: undefined;
    };

Component prop types

component-types.ts
// Single select props (simplified)
type TSearchableDropdown<T, G> =
  T extends TObjectLikeDropdownOption
    ? TSearchableCommon<T, G> & {
        searchOptionKeys: Array<Extract<keyof T, string>>;
      }
    : TSearchableCommon<T, G> & {
        searchOptionKeys?: undefined;
      };

// Multi select props (simplified)
type TSearchableDropdownMulti<T, G> =
  T extends TObjectLikeDropdownOption
    ? TSearchableCommonMulti<T, G> & {
        searchOptionKeys: Array<Extract<keyof T, string>>;
      }
    : TSearchableCommonMulti<T, G> & {
        searchOptionKeys?: undefined;
      };

The conditional type structure means that TypeScript will require searchOptionKeys when you pass object options and hide it when you pass string options. This is enforced at the type level: you literally cannot pass a meaningless searchOptionKeys to a string dropdown.

Wrapper prop types

The conditional types above (TSearchableDropdown, TSearchableDropdownMulti) enforce searchOptionKeys at compile time. However, TypeScript cannot resolve conditional types inside generic function bodies. If you are building a wrapper component that forwards props generically, use the wrapper-friendly types instead.

wrapper-types.ts
// Wrapper-friendly: searchOptionKeys is always optional.
// Use these when building generic wrapper components.
type TSearchableDropdownProps<T extends TDropdownOption, G = unknown>
type TSearchableDropdownMultiProps<T extends TDropdownOption, G = unknown>

Example: a wrapper that sets default props while staying generic over the option type.

WrapperExample.tsx
import {
  SearchableDropdown,
  type TSearchableDropdownProps,
  type TDropdownOption,
} from "@luciodale/react-searchable-dropdown";

function MyDropdown<T extends TDropdownOption, G>(
  props: TSearchableDropdownProps<T, G>
) {
  return (
    <SearchableDropdown
      dropdownOptionsHeight={320}
      placeholder="Search..."
      debounceDelay={100}
      {...props}
    />
  );
}

// Usage — still fully typed
<MyDropdown options={["a", "b", "c"]} value={undefined} setValue={setValue} />
<MyDropdown
  options={[{ label: "Red", value: "red" }]}
  value={undefined}
  setValue={setValue}
  searchOptionKeys={["label"]}
/>

CSS files

Importable stylesheets

Prop Type Default Description
dist/single-style.css CSS Default dark theme for SearchableDropdown. Includes variables.css.
dist/multi-style.css CSS Default dark theme for SearchableDropdownMulti. Includes variables.css and chip styles.
dist/variables.css CSS CSS custom properties only. Import this if you want the variable definitions without the component styles.

All type exports

type-imports.ts
import type {
  TDropdownOption,
  TNewValueDropdownOption,
  TObjectDropdownOption,
  TObjectLikeDropdownOption,
  TStringDropdownOption,
  TSearchableDropdown,
  TSearchableDropdownMulti,
  TSearchableDropdownProps,
  TSearchableDropdownMultiProps,
  TSearchableCommon,
  TSearchableCommonMulti,
  TSearchableDropdownFilterType,
  TSearchOptionKeys,
  TGroups,
} from "@luciodale/react-searchable-dropdown";