OptionsCombobox
Typed options wrapper around the UI combobox primitive.
Recipe Source
"use client";import type { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxItem, ComboboxList,} from "@giddaa-housing/ui/combobox";import type { ReactNode } from "react";import { getSelectableOptionKey, getSelectableOptionText, isSelectableOptionValueEqual, type SelectableOption,} from "./selectable-option";type OptionsComboboxProps<Option extends SelectableOption = SelectableOption> = Omit< ComboboxPrimitive.Root.Props<Option>, "children" | "defaultValue" | "items" | "onValueChange" | "value" > & Pick< React.ComponentProps<typeof ComboboxContent>, "side" | "align" | "alignOffset" > & { children?: ReactNode; options: readonly Option[]; className?: string; defaultValue?: Option["value"] | null; emptyMessage?: ReactNode; onValueChange?: ( value: Option["value"] | null, option: Option | null, ) => void; renderItem?: (option: Option) => ReactNode; value?: Option["value"] | null; };function OptionsCombobox<Option extends SelectableOption>({ defaultValue, value, options, className, side, align, alignOffset, emptyMessage = "No options available", itemToStringLabel = getSelectableOptionText, itemToStringValue = (option) => String(option.value), onValueChange, renderItem, children, ...props}: OptionsComboboxProps<Option>) { const selectedOption = value === undefined ? undefined : findOptionByValue(options, value); const defaultSelectedOption = defaultValue === undefined ? undefined : findOptionByValue(options, defaultValue); return ( <Combobox<Option> items={options} value={selectedOption} defaultValue={defaultSelectedOption} itemToStringLabel={itemToStringLabel} itemToStringValue={itemToStringValue} onValueChange={(option) => { onValueChange?.(option?.value ?? null, option ?? null); }} {...props} > {children} <ComboboxContent className={className} side={side} align={align} alignOffset={alignOffset} > <ComboboxEmpty>{emptyMessage}</ComboboxEmpty> <ComboboxList> {(option: Option) => ( <ComboboxItem key={getSelectableOptionKey(option)} value={option} disabled={option.disabled} > {renderItem ? renderItem(option) : option.label} </ComboboxItem> )} </ComboboxList> </ComboboxContent> </Combobox> );}function findOptionByValue<Option extends SelectableOption>( options: readonly Option[], value: Option["value"] | null,) { if (value == null) { return null; } return ( options.find((option) => isSelectableOptionValueEqual(option.value, value), ) ?? null );}export { OptionsCombobox, type OptionsComboboxProps };Usage
import { OptionsCombobox } from "@/components/recipes/options-combobox";
<OptionsCombobox options={[{ label: "Lekki", value: "lekki" }]} />Examples
Options Combobox
Use this when consumers have simple { label, value } options but still need combobox behavior.
Props
| Prop/API | Type | Default | Description |
|---|---|---|---|
options | SelectableOption[] | - | Options to render. |
value / defaultValue | option value | - | Controlled or uncontrolled selected value. |
renderItem | (option) => ReactNode | - | Custom list item rendering. |
emptyMessage | ReactNode | "No options available" | Empty state content. |
Accessibility
- Preserve combobox trigger/value children.
- Provide text values for non-string labels.
- Keep disabled options visibly and semantically disabled.